mirror of https://github.com/flysand7/ciabatta.git
Temporary files + Some work on threads.h header
This commit is contained in:
parent
d5650150b1
commit
3fe99b72cb
53
inc/stdio.h
53
inc/stdio.h
|
@ -3,56 +3,37 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
|
||||
#if !defined(NULL)
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
// 7.23.3 p.2: Header version
|
||||
#define __STDC_VERSION_STDIO_H__ 202311L
|
||||
|
||||
// 7.23.3 p.3: Types
|
||||
#if defined(_WIN32)
|
||||
typedef unsigned long long size_t;
|
||||
#else
|
||||
typedef unsigned long size_t;
|
||||
#endif
|
||||
|
||||
#if !defined(_mbstate_t_defined)
|
||||
#define _mbstate_t_defined
|
||||
typedef struct mbstate_t mbstate_t;
|
||||
struct mbstate_t {
|
||||
union {
|
||||
unsigned short leftover;
|
||||
unsigned short high_surrogate;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct FILE FILE;
|
||||
|
||||
typedef struct {
|
||||
unsigned long long offset;
|
||||
mbstate_t mbstate;
|
||||
union {
|
||||
unsigned short leftover;
|
||||
unsigned short high_surrogate;
|
||||
} mbstate;
|
||||
} fpos_t;
|
||||
|
||||
// 7.23.3 p.4: Macros
|
||||
#if !defined(NULL)
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_LIB_EXT1__)
|
||||
#define __STDC_LIB_EXT1__
|
||||
typedef int errno_t;
|
||||
typedef size_t rsize_t;
|
||||
#endif
|
||||
|
||||
#ifdef __STDC_WANT_SECURE_LIB__
|
||||
#if !defined(__STDC_WANT_LIB_EXT1__)
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define _IONBF 0
|
||||
#define _IOFBF 1
|
||||
#define _IOLBF 2
|
||||
#define BUFSIZ 512
|
||||
#define EOF (-1)
|
||||
#define FOPEN_MAX 1024
|
||||
#define FOPEN_MAX 32
|
||||
|
||||
#ifdef _os_win
|
||||
#define FILENAME_MAX 260
|
||||
|
@ -60,6 +41,8 @@ typedef struct {
|
|||
#define FILENAME_MAX 4096
|
||||
#endif
|
||||
|
||||
#define _PRINTF_NAN_LEN_MAX 20
|
||||
|
||||
#define L_tmpnam FILENAME_MAX
|
||||
|
||||
#define SEEK_SET 0
|
||||
|
@ -72,17 +55,21 @@ typedef struct {
|
|||
#define TMP_MAX INT_MAX
|
||||
#endif
|
||||
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
extern FILE *stdin;
|
||||
#define stdout _internal_stdout
|
||||
#define stderr _internal_stderr
|
||||
#define stdin _internal_stdin
|
||||
|
||||
extern FILE *_internal_stdout;
|
||||
extern FILE *_internal_stderr;
|
||||
extern FILE *_internal_stdin;
|
||||
|
||||
// File manipulation
|
||||
int remove(const char *filename);
|
||||
int rename(const char *oldname, const char *newname);
|
||||
FILE *tmpfile(void);
|
||||
char *tmpnam(char *s);
|
||||
|
||||
// Opening and closing files
|
||||
char *tmpnam(char *s);
|
||||
FILE *tmpfile(void);
|
||||
FILE *fopen (const char *restrict filename, const char *restrict mode);
|
||||
FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream);
|
||||
int fclose (FILE *stream);
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#define thread_local _Thread_local
|
||||
// 7.28.1 p.3: Macros
|
||||
|
||||
#define ONCE_FLAG_INIT 1
|
||||
#define TSS_DTOR_ITERATIONS 32
|
||||
|
||||
// TODO(bumbread): check the spec for whether thread_local needs to be declared
|
||||
// in threads.h
|
||||
#define thread_local _Thread_local
|
||||
|
||||
// 7.28.1 p.4: Types
|
||||
|
||||
#if !defined(_timespec_defined)
|
||||
#define _timespec_defined
|
||||
typedef unsigned long long time_t;
|
||||
|
@ -14,6 +20,7 @@ struct timespec {
|
|||
};
|
||||
#endif
|
||||
|
||||
// TODO(bumbread): move this out to platform-specific folder
|
||||
#if defined(_WIN32)
|
||||
typedef struct cnd_t {
|
||||
int idk_yet;
|
||||
|
@ -41,7 +48,12 @@ typedef struct mtx_t {
|
|||
|
||||
typedef void(*tss_dtor_t) (void*);
|
||||
typedef int (*thrd_start_t)(void*);
|
||||
typedef int once_flag; // TODO: change this maybe?
|
||||
|
||||
// TODO(bumbread): this probably should be a mutex or a semaphore
|
||||
// also probably can be implemented with interlocked increment
|
||||
typedef int once_flag;
|
||||
|
||||
// 7.28.1 p.5: Enumeration constants
|
||||
|
||||
enum {
|
||||
mtx_plain = 0,
|
||||
|
@ -57,29 +69,7 @@ enum {
|
|||
thrd_nomem
|
||||
};
|
||||
|
||||
void call_once(once_flag *flag, void (*func)(void));
|
||||
|
||||
int cnd_init (cnd_t *cond);
|
||||
int cnd_broadcast(cnd_t *cond);
|
||||
void cnd_destroy (cnd_t *cond);
|
||||
int cnd_signal (cnd_t *cond);
|
||||
int cnd_wait (cnd_t *cond, mtx_t *mtx);
|
||||
int cnd_timedwait(
|
||||
cnd_t *restrict cond,
|
||||
mtx_t *restrict mtx,
|
||||
const struct timespec *restrict ts
|
||||
);
|
||||
|
||||
void mtx_destroy (mtx_t *mtx);
|
||||
int mtx_init (mtx_t *mtx, int type);
|
||||
int mtx_unlock (mtx_t *mtx);
|
||||
int mtx_trylock (mtx_t *mtx);
|
||||
int mtx_lock (mtx_t *mtx);
|
||||
int mtx_timedlock(
|
||||
mtx_t *restrict mtx,
|
||||
const struct timespec *restrict ts
|
||||
);
|
||||
|
||||
// Thread functions
|
||||
thrd_t thrd_current(void);
|
||||
int thrd_create (thrd_t *thr, thrd_start_t func, void *arg);
|
||||
int thrd_detach (thrd_t thr);
|
||||
|
@ -92,7 +82,34 @@ int thrd_sleep(
|
|||
);
|
||||
_Noreturn void thrd_exit(int res);
|
||||
|
||||
// Mutex functions
|
||||
void mtx_destroy (mtx_t *mtx);
|
||||
int mtx_init (mtx_t *mtx, int type);
|
||||
int mtx_unlock (mtx_t *mtx);
|
||||
int mtx_trylock (mtx_t *mtx);
|
||||
int mtx_lock (mtx_t *mtx);
|
||||
int mtx_timedlock(
|
||||
mtx_t *restrict mtx,
|
||||
const struct timespec *restrict ts
|
||||
);
|
||||
|
||||
// Thread-local storage functions
|
||||
int tss_create(tss_t *key, tss_dtor_t dtor);
|
||||
void tss_delete(tss_t key);
|
||||
void *tss_get (tss_t key);
|
||||
int tss_set (tss_t key, void *val);
|
||||
|
||||
// Condition functions
|
||||
int cnd_init (cnd_t *cond);
|
||||
int cnd_broadcast(cnd_t *cond);
|
||||
void cnd_destroy (cnd_t *cond);
|
||||
int cnd_signal (cnd_t *cond);
|
||||
int cnd_wait (cnd_t *cond, mtx_t *mtx);
|
||||
int cnd_timedwait(
|
||||
cnd_t *restrict cond,
|
||||
mtx_t *restrict mtx,
|
||||
const struct timespec *restrict ts
|
||||
);
|
||||
|
||||
// Call once
|
||||
void call_once(once_flag *flag, void (*func)(void));
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
#include "conv/strpfx.c"
|
||||
#include "conv/int.c"
|
||||
#include "conv/float.c"
|
||||
#include "fmt/fmt.c"
|
||||
#include "stdlib/algorithm.c"
|
||||
#include "stdlib/multibyte.c"
|
||||
#include "stdlib/random.c"
|
||||
|
@ -69,11 +68,11 @@
|
|||
#include "os_win/cookie.c"
|
||||
#include "os_win/assert.c"
|
||||
#include "os_win/cmdline.c"
|
||||
#include "os_win/entry.c"
|
||||
#include "os_win/environment.c"
|
||||
#include "os_win/heap.c"
|
||||
#include "os_win/signal.c"
|
||||
#include "os_win/stdio.c"
|
||||
#include "os_win/threads.c"
|
||||
#include "os_win/file.c"
|
||||
#include "os_win/thread.c"
|
||||
#include "os_win/time.c"
|
||||
#include "os_win/entry.c"
|
||||
#endif
|
||||
|
|
164
src/fmt/fmt.c
164
src/fmt/fmt.c
|
@ -1,164 +0,0 @@
|
|||
|
||||
#define FLAG_ZERO 0x0001 // "0"
|
||||
#define FLAG_LEFT 0x0002 // "-"
|
||||
#define FLAG_PLUS 0x0004 // "+"
|
||||
#define FLAG_SPACE 0x0008 // " "
|
||||
#define FLAG_HASH 0x0010 // "#"
|
||||
#define FLAG_PREC 0x0020 // ".precision"
|
||||
#define FLAG_LONG 0x0040 // "l"
|
||||
#define FLAG_LLONG 0x0080 // "ll"
|
||||
#define FLAG_SHORT 0x0100 // "h"
|
||||
#define FLAG_CHAR 0x0200 // "hh"
|
||||
#define FLAG_UPPER 0x0400 // for hex digits 0xa-0xf
|
||||
|
||||
struct fmt_t typedef fmt_t;
|
||||
struct fmt_t {
|
||||
int prec;
|
||||
int width;
|
||||
int flags;
|
||||
};
|
||||
|
||||
#define ctype char
|
||||
#define pfx(f) f
|
||||
#include "fmt_string.h"
|
||||
#include "fmt_stream.h"
|
||||
#include "ints.h"
|
||||
#include "fmt.h"
|
||||
#undef ctype
|
||||
#undef pfx
|
||||
|
||||
#define ctype wchar_t
|
||||
#define pfx(f) w ## f
|
||||
#include "fmt_string.h"
|
||||
#include "fmt_stream.h"
|
||||
#include "ints.h"
|
||||
#include "fmt.h"
|
||||
#undef ctype
|
||||
#undef pfx
|
||||
|
||||
// fprintf family
|
||||
|
||||
static inline int fstream_outc(void *ctx, char c) {
|
||||
FILE *file = ctx;
|
||||
int result = fputc(c, file);
|
||||
if(result == EOF) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int fstream_outw(void *ctx, wchar c) {
|
||||
FILE *file = ctx;
|
||||
int result = fputc((char)c, file); // TODO: utf8
|
||||
if(result == EOF) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline fmt_stream_t fstream_new(FILE *file) {
|
||||
fmt_stream_t stream;
|
||||
stream.w = 0;
|
||||
stream.ctx = file;
|
||||
stream.out_ctype = fstream_outc;
|
||||
stream.out_wchar = fstream_outw;
|
||||
stream.out_char = fstream_outc;
|
||||
return stream;
|
||||
}
|
||||
|
||||
int vfprintf(FILE *restrict file, const char *restrict string, va_list va) {
|
||||
fmt_stream_t stream = fstream_new(file);
|
||||
return fmt(&stream, string, va);
|
||||
}
|
||||
|
||||
int vprintf(const char *restrict string, va_list va) {
|
||||
return vfprintf(stdout, string, va);
|
||||
}
|
||||
|
||||
int fprintf(FILE *restrict file, const char *restrict string, ...) {
|
||||
va_list va;
|
||||
va_start(va, string);
|
||||
int result = vfprintf(file, string, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
int printf(const char *restrict string, ...) {
|
||||
va_list va;
|
||||
va_start(va, string);
|
||||
int result = vfprintf(stdout, string, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
// s(n)printf family
|
||||
|
||||
struct str_slice_t typedef str_slice_t;
|
||||
struct str_slice_t {
|
||||
char *str;
|
||||
size_t max_size;
|
||||
size_t written;
|
||||
};
|
||||
|
||||
static inline int sstream_outc(void *ctx, char c) {
|
||||
str_slice_t *slice = ctx;
|
||||
if(slice->written+1 < slice->max_size) {
|
||||
return -1;
|
||||
}
|
||||
slice->str[slice->written++] = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int sstream_outw(void *ctx, wchar c) {
|
||||
str_slice_t *slice = ctx;
|
||||
// TODO: utf8
|
||||
if(slice->written+1 < slice->max_size) {
|
||||
return -1;
|
||||
}
|
||||
slice->str[slice->written++] = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline fmt_stream_t sstream_new(str_slice_t *slice) {
|
||||
fmt_stream_t stream;
|
||||
stream.w = 0;
|
||||
stream.ctx = slice;
|
||||
stream.out_ctype = sstream_outc;
|
||||
stream.out_wchar = sstream_outw;
|
||||
stream.out_char = sstream_outc;
|
||||
return stream;
|
||||
}
|
||||
|
||||
int vsnprintf(char *restrict buf, size_t cbbuf, const char *restrict str, va_list va) {
|
||||
str_slice_t slice;
|
||||
slice.str = buf;
|
||||
slice.max_size = cbbuf;
|
||||
slice.written = 0;
|
||||
fmt_stream_t stream = sstream_new(&slice);
|
||||
return fmt(&stream, str, va);
|
||||
}
|
||||
|
||||
int vsprintf(char *restrict buf, const char *restrict str, va_list va) {
|
||||
str_slice_t slice;
|
||||
slice.str = buf;
|
||||
slice.max_size = SIZE_MAX;
|
||||
slice.written = 0;
|
||||
fmt_stream_t stream = sstream_new(&slice);
|
||||
return fmt(&stream, str, va);
|
||||
}
|
||||
|
||||
int snprintf(char *restrict buf, size_t cbbuf, const char *restrict str, ...) {
|
||||
va_list va;
|
||||
va_start(va, str);
|
||||
int result = vsnprintf(buf, cbbuf, str, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
int sprintf(char *restrict buf, const char *restrict str, ...) {
|
||||
va_list va;
|
||||
va_start(va, str);
|
||||
int result = vsprintf(buf, str, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
|
||||
static inline int pfx(fmt_arg)(fmt_stream_t *stream, ctype const **strp, va_list va) {
|
||||
ctype const *str = *strp;
|
||||
fmt_t fmt = {0};
|
||||
if(!pfx(fmt_read)(str, &fmt, va)) {
|
||||
return 0;
|
||||
}
|
||||
switch(*str) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'b':
|
||||
case 'o':
|
||||
case 'd':
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
int base;
|
||||
int sign;
|
||||
u64 abs = pfx(fmt_int_arg)(stream, &fmt, &str, &sign, &base, va);
|
||||
if(pfx(fmt_int)(stream, fmt, sign, abs, base) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
*strp = str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int pfx(fmt)(fmt_stream_t *stream, ctype const *str, va_list va) {
|
||||
stream->w = 0;
|
||||
while(*str) {
|
||||
if(*str == '%') {
|
||||
++str;
|
||||
int ok = pfx(fmt_arg)(stream, &str, va);
|
||||
if(!ok) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
stream_out(stream, *str++);
|
||||
}
|
||||
}
|
||||
return stream->w;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
struct pfx(fmt_stream_t) typedef pfx(fmt_stream_t);
|
||||
struct pfx(fmt_stream_t) {
|
||||
int w;
|
||||
void *ctx;
|
||||
int (*pfx(out_ctype))(void *ctx, ctype ch);
|
||||
int (*pfx(out_wchar))(void *ctx, wchar ch);
|
||||
int (*pfx(out_char)) (void *ctx, char ch);
|
||||
};
|
||||
|
||||
#define stream_out(s, ch) if((s->w++, !s->out_ctype(s->ctx, ch))) return -1
|
||||
#define stream_outc(s, ch) if((s->w++, !s->out_char (s->ctx, ch))) return -1
|
||||
#define stream_outw(s, ch) if((s->w++, !s->out_wchar(s->ctx, ch))) return -1
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
static inline int pfx(fmt_isdigit)(ctype ch) {
|
||||
return '0' <= ch && ch <= '9';
|
||||
}
|
||||
|
||||
static inline int pfx(fmt_atoi)(ctype const **sp, int *err) {
|
||||
int i = 0;
|
||||
ctype const *s = *sp;
|
||||
while(pfx(fmt_isdigit(*s))) {
|
||||
int d = *s-'0';
|
||||
if(i > (INT_MAX - d)/10) {
|
||||
*err = 1;
|
||||
return 0;
|
||||
}
|
||||
i = 10*i + d;
|
||||
++s;
|
||||
}
|
||||
*sp = s;
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int pfx(fmt_read)(const ctype *str, fmt_t *fmt, va_list va) {
|
||||
int flags = 0;
|
||||
// Parse flag field
|
||||
{
|
||||
int isflag = 0;
|
||||
do {
|
||||
isflag = 1;
|
||||
switch(*str) {
|
||||
case '0': flags |= FLAG_ZERO; break;
|
||||
case '-': flags |= FLAG_LEFT; break;
|
||||
case '+': flags |= FLAG_PLUS; break;
|
||||
case ' ': flags |= FLAG_SPACE; break;
|
||||
case '#': flags |= FLAG_HASH; break;
|
||||
default: isflag = 0;
|
||||
}
|
||||
if(isflag) ++str;
|
||||
} while(isflag);
|
||||
// If '-' and '0' are together we just discard '0'
|
||||
if(flags & FLAG_LEFT) flags &= ~FLAG_ZERO;
|
||||
}
|
||||
// Parse width field
|
||||
int width = 0;
|
||||
if(pfx(fmt_isdigit(*str))) {
|
||||
int err = 0;
|
||||
width = pfx(fmt_atoi(&str, &err));
|
||||
if(err) return 0;
|
||||
}
|
||||
else if(*str == '*') {
|
||||
++str;
|
||||
width = va_arg(va, int);
|
||||
if(width < 0) {
|
||||
width = -width;
|
||||
flags |= FLAG_LEFT;
|
||||
}
|
||||
}
|
||||
// If present, parse the precision field
|
||||
int prec = 0;
|
||||
if(*str == '.') {
|
||||
flags |= FLAG_PREC;
|
||||
++str;
|
||||
if(pfx(fmt_isdigit(*str))) {
|
||||
int err = 0;
|
||||
prec = pfx(fmt_atoi(&str, &err));
|
||||
if(err) return -1;
|
||||
}
|
||||
else if(*str == '*') {
|
||||
++str;
|
||||
prec = va_arg(va, int);
|
||||
prec = (prec > 0? prec : 0);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
|
||||
#define ctype char
|
||||
#define pfx(f) f
|
||||
#include "gen_fmt.h"
|
||||
#undef ctype
|
||||
#undef pfx
|
||||
|
||||
#define ctype wchar_t
|
||||
#define pfx(f) w ## f
|
||||
#include "gen_fmt.h"
|
||||
#undef ctype
|
||||
#undef pfx
|
||||
|
||||
|
||||
static int fprintfcb(void *ctx, char ch) {
|
||||
FILE *f = ctx;
|
||||
return fputc(ch, f) != EOF;
|
||||
}
|
||||
|
||||
struct str_ctx_t typedef str_ctx_t;
|
||||
struct str_ctx_t {
|
||||
char *str;
|
||||
size_t n;
|
||||
size_t i;
|
||||
};
|
||||
|
||||
static int sprintfcb(void *ctx, char ch) {
|
||||
str_ctx_t *stream = ctx;
|
||||
stream->str[stream->i++] = ch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int snprintfcb(void *ctx, char ch) {
|
||||
str_ctx_t *stream = ctx;
|
||||
if(stream->i >= stream->n) {
|
||||
return 0;
|
||||
}
|
||||
stream->str[stream->i++] = ch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int vfprintf(FILE *stream, const char *fmt, va_list args) {
|
||||
return vprintfcb(stream, fprintfcb, fmt, args);
|
||||
}
|
||||
|
||||
int vprintf(const char *fmt, va_list args) {
|
||||
return vprintfcb(stdout, fprintfcb, fmt, args);
|
||||
}
|
||||
|
||||
int fprintf(FILE *stream, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vfprintf(stream, fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
int printf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int vsprintf(char *buf, char const *fmt, va_list args) {
|
||||
str_ctx_t ctx = {0};
|
||||
ctx.str = buf;
|
||||
return vprintfcb(&ctx, sprintfcb, fmt, args);
|
||||
}
|
||||
|
||||
int vsnprintf(char *buf, size_t sz, char const *fmt, va_list args) {
|
||||
str_ctx_t ctx = {0};
|
||||
ctx.str = buf;
|
||||
ctx.n = sz;
|
||||
return vprintfcb(&ctx, snprintfcb, fmt, args);
|
||||
}
|
||||
|
||||
int sprintf(char *buf, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
int snprintf(char *buf, size_t sz, char const *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsnprintf(buf, sz, fmt, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
|
@ -1,883 +0,0 @@
|
|||
|
||||
typedef int (pfx(cbfn))(void *ctx, ctype ch);
|
||||
|
||||
#define FLAG_ZERO 0x0001 // "0"
|
||||
#define FLAG_LEFT 0x0002 // "-"
|
||||
#define FLAG_PLUS 0x0004 // "+"
|
||||
#define FLAG_SPACE 0x0008 // " "
|
||||
#define FLAG_HASH 0x0010 // "#"
|
||||
#define FLAG_PREC 0x0020 // ".precision"
|
||||
#define FLAG_LONG 0x0040 // "l"
|
||||
#define FLAG_LLONG 0x0080 // "ll"
|
||||
#define FLAG_SHORT 0x0100 // "h"
|
||||
#define FLAG_CHAR 0x0200 // "hh"
|
||||
#define FLAG_UPPER 0x0400 // for hex digits 0xa-0xf
|
||||
|
||||
// Helper function declaration
|
||||
static inline int pfx(_isdigit)(ctype ch);
|
||||
static inline int pfx(_atoi)(ctype const **sp, int *err);
|
||||
static inline int pfx(_ntoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
int neg,
|
||||
unsigned long long value,
|
||||
int base,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
);
|
||||
static inline int pfx(_dtoh)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
double value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
);
|
||||
static inline int pfx(_dtoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
decfloat_t value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
);
|
||||
static inline int pfx(_etoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
decfloat_t value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
);
|
||||
static inline int pfx(_infnantoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
double value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
);
|
||||
|
||||
#define out(ch) do { if((w++, !cb(ctx, (ch)))) return -1; } while(0)
|
||||
|
||||
#define print_lwidth_spaces(flags, pad) \
|
||||
do if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad-- > 0) { \
|
||||
out(' '); \
|
||||
} \
|
||||
while(0)
|
||||
|
||||
#define print_lwidth_zeros(flags, pad) \
|
||||
do if(!(flags & FLAG_LEFT) && (flags & FLAG_ZERO)) while(pad-- > 0) { \
|
||||
out('0'); \
|
||||
} \
|
||||
while(0)
|
||||
|
||||
#define print_rwidth(flags, pad) \
|
||||
if(flags & FLAG_LEFT) while(pad-- > 0) { \
|
||||
out(' '); \
|
||||
}
|
||||
|
||||
// Generic print
|
||||
static int pfx(vprintfcb)(
|
||||
void *ctx,
|
||||
pfx(cbfn) *cb,
|
||||
const ctype *fmt,
|
||||
va_list va
|
||||
) {
|
||||
int w = 0;
|
||||
while(*fmt) {
|
||||
// Write a character if it's not a '%' sign
|
||||
while(*fmt && *fmt != '%') {
|
||||
out(*fmt++);
|
||||
continue;
|
||||
}
|
||||
if(*fmt == '%') {
|
||||
++fmt;
|
||||
}
|
||||
// Parse flags
|
||||
int flags = 0;
|
||||
int isflag = 0;
|
||||
do {
|
||||
isflag = 1;
|
||||
switch(*fmt) {
|
||||
case '0': flags |= FLAG_ZERO; break;
|
||||
case '-': flags |= FLAG_LEFT; break;
|
||||
case '+': flags |= FLAG_PLUS; break;
|
||||
case ' ': flags |= FLAG_SPACE; break;
|
||||
case '#': flags |= FLAG_HASH; break;
|
||||
default: isflag = 0;
|
||||
}
|
||||
if(isflag) ++fmt;
|
||||
} while(isflag);
|
||||
if(flags & FLAG_LEFT) flags &= ~FLAG_ZERO;
|
||||
// Parse width field
|
||||
int width = 0;
|
||||
if(pfx(_isdigit(*fmt))) {
|
||||
int err = 0;
|
||||
width = pfx(_atoi(&fmt, &err));
|
||||
if(err) return -1;
|
||||
}
|
||||
else if(*fmt == '*') {
|
||||
++fmt;
|
||||
width = va_arg(va, int);
|
||||
if(width < 0) {
|
||||
width = -width;
|
||||
flags |= FLAG_LEFT;
|
||||
}
|
||||
}
|
||||
// If present, parse the precision field
|
||||
int prec = 0;
|
||||
if(*fmt == '.') {
|
||||
flags |= FLAG_PREC;
|
||||
++fmt;
|
||||
if(pfx(_isdigit(*fmt))) {
|
||||
int err = 0;
|
||||
prec = pfx(_atoi(&fmt, &err));
|
||||
if(err) return -1;
|
||||
}
|
||||
else if(*fmt == '*') {
|
||||
++fmt;
|
||||
prec = va_arg(va, int);
|
||||
prec = (prec > 0? prec : 0);
|
||||
}
|
||||
}
|
||||
// Parse length field
|
||||
switch(*fmt) {
|
||||
case 'l':
|
||||
++fmt;
|
||||
flags |= FLAG_LONG;
|
||||
if(*fmt == 'l') {
|
||||
++fmt;
|
||||
flags |= FLAG_LLONG;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
++fmt;
|
||||
flags |= FLAG_SHORT;
|
||||
if(*fmt == 'h') {
|
||||
++fmt;
|
||||
flags |= FLAG_CHAR;
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
++fmt;
|
||||
if(sizeof(intmax_t) == sizeof(long))
|
||||
flags |= FLAG_LONG;
|
||||
else flags |= FLAG_LLONG;
|
||||
break;
|
||||
case 'z':
|
||||
++fmt;
|
||||
if(sizeof(size_t) == sizeof(long))
|
||||
flags |= FLAG_LONG;
|
||||
else flags |= FLAG_LLONG;
|
||||
break;
|
||||
case 't':
|
||||
++fmt;
|
||||
if(sizeof(ptrdiff_t) == sizeof(long))
|
||||
flags |= FLAG_LONG;
|
||||
else flags |= FLAG_LLONG;
|
||||
break;
|
||||
case 'L':
|
||||
++fmt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Print according to specifier
|
||||
switch(*fmt) {
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'b': {
|
||||
// Figure out the signedness and base
|
||||
int base = 10;
|
||||
if(*fmt == 'x' || *fmt == 'X') base = 16;
|
||||
if(*fmt == 'o') base = 8;
|
||||
if(*fmt == 'b') base = 2;
|
||||
int signd = (*fmt == 'i' || *fmt == 'd');
|
||||
// Figure out flags
|
||||
if(*fmt == 'X') flags |= FLAG_UPPER;
|
||||
if(base == 10) flags &= ~FLAG_HASH;
|
||||
if(!signd) flags &= ~(FLAG_PLUS|FLAG_SPACE);
|
||||
if(flags & FLAG_PREC) flags &= ~FLAG_ZERO;
|
||||
++fmt;
|
||||
// Read-in the integer argument from var args
|
||||
int neg = 0;
|
||||
unsigned long long value = 0;
|
||||
if(signd) {
|
||||
long long num;
|
||||
if(flags & FLAG_LLONG)
|
||||
num = (long long)va_arg(va, long long);
|
||||
else if(flags & FLAG_LONG)
|
||||
num = (long long)va_arg(va, long);
|
||||
else
|
||||
num = (long long)va_arg(va, int);
|
||||
// Avoiding UB on unary negation like a bitch
|
||||
if(num == LLONG_MIN) {
|
||||
neg = 1;
|
||||
value = (unsigned long long)LLONG_MAX + 1;
|
||||
}
|
||||
else {
|
||||
neg = num >= 0? 0 : 1;
|
||||
num = num >= 0? num : -num;
|
||||
value = (unsigned long long)num;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(flags & FLAG_LLONG)
|
||||
value = (unsigned long long)va_arg(va, unsigned long long);
|
||||
else if(flags & FLAG_LONG)
|
||||
value = (unsigned long long)va_arg(va, unsigned long);
|
||||
else
|
||||
value = (unsigned long long)va_arg(va, unsigned int);
|
||||
}
|
||||
// Output integer
|
||||
w = pfx(_ntoa)(w, ctx, cb, neg, value, base, prec, width, flags);
|
||||
if(w < 0) return -1;
|
||||
} break;
|
||||
case 'a':
|
||||
case 'A': {
|
||||
if(*fmt == 'A') flags |= FLAG_UPPER;
|
||||
++fmt;
|
||||
double value = va_arg(va, double);
|
||||
w = pfx(_dtoh)(w, ctx, cb, value, prec, width, flags);
|
||||
if(w < 0) return -1;
|
||||
} break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G': {
|
||||
if(*fmt == 'E' || *fmt == 'F' || *fmt == 'G') flags |= FLAG_UPPER;
|
||||
char conv = tolower(*fmt);
|
||||
++fmt;
|
||||
double value = va_arg(va, double);
|
||||
int class = fpclassify(value);
|
||||
if(class == FP_INFINITE || class == FP_NAN) {
|
||||
return pfx(_infnantoa)(w, ctx, cb, value, prec, width, flags);
|
||||
}
|
||||
decfloat_t dec_value = f64_to_decimal(value);
|
||||
if(conv == 'f') {
|
||||
w = pfx(_dtoa)(w, ctx, cb, dec_value, prec, width, flags);
|
||||
}
|
||||
else if(conv == 'e') {
|
||||
w = pfx(_etoa)(w, ctx, cb, dec_value, prec, width, flags);
|
||||
}
|
||||
else {
|
||||
int P = prec;
|
||||
if(!(flags & FLAG_PREC)) P = 6;
|
||||
if(prec == 0) P = 1;
|
||||
int64_t E;
|
||||
{
|
||||
if(class == FP_ZERO) {
|
||||
E = 0;
|
||||
}
|
||||
else {
|
||||
E = dec_value.exponent;
|
||||
}
|
||||
}
|
||||
if(class == FP_SUBNORMAL || class == FP_ZERO) {
|
||||
E = 0;
|
||||
}
|
||||
flags |= FLAG_PREC;
|
||||
if(P > E && E >= -4) {
|
||||
w = pfx(_dtoa)(w, ctx, cb, dec_value, P-(E+1), width, flags);
|
||||
}
|
||||
else {
|
||||
w = pfx(_etoa)(w, ctx, cb, dec_value, P-1, width, flags);
|
||||
}
|
||||
}
|
||||
if(w < 0) return -1;
|
||||
}break;
|
||||
case 'c': {
|
||||
++fmt;
|
||||
int ch = va_arg(va, int);
|
||||
int pad = width-1;
|
||||
// Print width left-pad
|
||||
if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
out(ch);
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
} break;
|
||||
case 's': {
|
||||
++fmt;
|
||||
char *str = NULL;
|
||||
wchar_t *wstr = NULL;
|
||||
int len = 0;
|
||||
if(flags & FLAG_LONG) {
|
||||
wstr = va_arg(va, wchar_t *);
|
||||
wchar_t *s = wstr;
|
||||
while(*s++!=0) ++len;
|
||||
int pad = width - len;
|
||||
// Print width left-pad
|
||||
if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
// Print string
|
||||
mbstate_t ps = {0};
|
||||
for(int i = 0; i < len; ++i) {
|
||||
if(sizeof(ctype) == 2) {
|
||||
out(wstr[i]);
|
||||
}
|
||||
else {
|
||||
// char utf8[5];
|
||||
// char *s = utf8;
|
||||
// size_t r = c16rtomb(s, wstr[i], &ps);
|
||||
// if(r == (size_t)(-1)) return -1;
|
||||
// if(r != 0) {
|
||||
// while(*s != 0) {
|
||||
// out(*s++);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
}
|
||||
else {
|
||||
str = va_arg(va, char *);
|
||||
char *s = str;
|
||||
if(flags & FLAG_PREC) {
|
||||
while(*s++!=0 && len<prec) ++len;
|
||||
}
|
||||
else {
|
||||
while(*s++!=0) ++len;
|
||||
}
|
||||
int pad = width - len;
|
||||
// Print width left-pad
|
||||
if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
// Print string
|
||||
//mbstate_t ps = {0};
|
||||
for(int i = 0; i < len; ++i) {
|
||||
out(str[i]);
|
||||
}
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 'p': {
|
||||
++fmt;
|
||||
void *ptr = va_arg(va, void *);
|
||||
unsigned long long iptr = (unsigned long long)(uintptr_t)ptr;
|
||||
w = pfx(_ntoa)(w, ctx, cb, 0, iptr, 16, 16, 0, FLAG_HASH);
|
||||
if(w < 0) return -1;
|
||||
} break;
|
||||
case 'n': {
|
||||
++fmt;
|
||||
int *p = va_arg(va, int *);
|
||||
*p = w;
|
||||
} break;
|
||||
case '%': {
|
||||
++fmt;
|
||||
out('%');
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline int pfx(_isdigit)(ctype ch) {
|
||||
return '0' <= ch && ch <= '9';
|
||||
}
|
||||
|
||||
static inline int pfx(_atoi)(ctype const **sp, int *err) {
|
||||
int i = 0;
|
||||
ctype const *s = *sp;
|
||||
while(pfx(_isdigit(*s))) {
|
||||
int d = *s-'0';
|
||||
if(i > (INT_MAX - d)/10) {
|
||||
*err = 1;
|
||||
return 0;
|
||||
}
|
||||
i = 10*i + d;
|
||||
++s;
|
||||
}
|
||||
*sp = s;
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int pfx(_ntoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
int neg,
|
||||
unsigned long long value,
|
||||
int base,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
) {
|
||||
// We'll Write digits into buf in reverse and then call _out_rbuf
|
||||
int ndigits = 0;
|
||||
static ctype digits[64];
|
||||
// Remove hash flag for 0 values
|
||||
if(value == 0) flags &= ~FLAG_HASH;
|
||||
// Write digits to buffer in reverse (starting from least significant)
|
||||
do {
|
||||
int d = (int)(value%base);
|
||||
value /= base;
|
||||
if(d < 10)
|
||||
d += '0';
|
||||
else if(flags & FLAG_UPPER)
|
||||
d += 'A' - 10;
|
||||
else d += 'a' - 10;
|
||||
digits[ndigits++] = d;
|
||||
} while(value);
|
||||
// Figure out the length of the prefix (part of number before digit stirng)
|
||||
int pref_len = 0;
|
||||
if(flags & FLAG_HASH) pref_len = base == 8? 1 : 2;
|
||||
else if(neg) pref_len = 1;
|
||||
else if(flags & FLAG_PLUS) pref_len = 1;
|
||||
else if(flags & FLAG_SPACE) pref_len = 1;
|
||||
if(ndigits > prec) prec = ndigits;
|
||||
int num_len = pref_len + prec;
|
||||
int pad_len = width - num_len;
|
||||
if(!(flags & FLAG_LEFT)) {
|
||||
ctype pad_ch = ' ';
|
||||
if(flags & FLAG_ZERO) pad_ch = '0';
|
||||
while(pad_len-- > 0) {
|
||||
out(pad_ch);
|
||||
}
|
||||
}
|
||||
// Print width left-pad if it's made out of space
|
||||
if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad_len-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
// Print prefix
|
||||
if(flags & FLAG_HASH) {
|
||||
out('0');
|
||||
if(base == 16) { out('x'); }
|
||||
else if(base == 2) { out('b'); }
|
||||
}
|
||||
else if(neg) { out('-'); }
|
||||
else if(flags & FLAG_PLUS) { out('+'); }
|
||||
else if(flags & FLAG_SPACE) { out(' '); }
|
||||
// Print width left-pad if it's made out of zero
|
||||
if(!(flags & FLAG_LEFT) && (flags & FLAG_ZERO)) while(pad_len-- > 0) {
|
||||
out('0');
|
||||
}
|
||||
// Print zero-pad due to precision
|
||||
for(int i = ndigits; i < prec; ++i) {
|
||||
out('0');
|
||||
}
|
||||
// Output buffer in reverse
|
||||
if(ndigits) while(ndigits--) {
|
||||
out(digits[ndigits]);
|
||||
}
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) while(pad_len-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline int pfx(_infnantoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
double value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
) {
|
||||
char const *name = NULL;
|
||||
int nchars = 0;
|
||||
if(isinf(value)) name = "inf", nchars = 3;
|
||||
if(isnan(value)) name = "nan", nchars = 3;
|
||||
// Figure out prefix
|
||||
int pref_len = 0;
|
||||
u64 bits = F64_BITS(value);
|
||||
u64 mant = F64_MANT(bits);
|
||||
int neg = F64_SIGN(bits) & (mant == 0);
|
||||
if(neg) pref_len = 1;
|
||||
else if(flags & FLAG_PLUS) pref_len = 1;
|
||||
else if(flags & FLAG_SPACE) pref_len = 1;
|
||||
// Figure out pad
|
||||
int str_len = pref_len + nchars;
|
||||
int pad_len = width - str_len;
|
||||
// Print left-pad
|
||||
if(!(flags & FLAG_LEFT)) {
|
||||
while(pad_len-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
}
|
||||
// Print the prefix
|
||||
if(neg) out('-');
|
||||
else if(flags & FLAG_PLUS) out('+');
|
||||
else if(flags & FLAG_SPACE) out(' ');
|
||||
// Print the string
|
||||
while(*name) {
|
||||
out(*name++);
|
||||
}
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) {
|
||||
while(pad_len-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline int pfx(_dtoh)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
double value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
) {
|
||||
// Filter out inifnities and NaN
|
||||
{
|
||||
int class = fpclassify(value);
|
||||
if(class == FP_INFINITE || class == FP_NAN) {
|
||||
return pfx(_infnantoa)(w, ctx, cb, value, prec, width, flags);
|
||||
}
|
||||
}
|
||||
// Deconstruct the float
|
||||
u64 bits = F64_BITS(value);
|
||||
u64 flt_sgn = F64_SIGN(bits);
|
||||
u64 flt_mnt = F64_MANT(bits);
|
||||
i64 flt_exp = F64_BEXP(bits);
|
||||
u64 frc;
|
||||
u64 mnt;
|
||||
i64 exp;
|
||||
{
|
||||
mnt = flt_mnt;
|
||||
exp = flt_exp - 0x3ff;
|
||||
if(flt_exp == 0 && flt_mnt == 0) {
|
||||
exp = 0;
|
||||
}
|
||||
}
|
||||
// Calculate widths of different number parts and extract some data
|
||||
static ctype frc_buffer[64] = {0};
|
||||
static ctype exp_buffer[64] = {0};
|
||||
ctype *frc_buf = frc_buffer + sizeof frc_buffer;
|
||||
ctype *exp_buf = exp_buffer + sizeof exp_buffer;
|
||||
ctype sgn_ch;
|
||||
ctype exp_sgn_ch;
|
||||
int has_sgn;
|
||||
int has_point;
|
||||
int nfrc = 0;
|
||||
int nexp = 0;
|
||||
int nfrcp = 0;
|
||||
{
|
||||
// Sign
|
||||
if(flt_sgn) {
|
||||
has_sgn = 1;
|
||||
sgn_ch = '-';
|
||||
}
|
||||
else if(flags & FLAG_PLUS) {
|
||||
has_sgn = 1;
|
||||
sgn_ch = '+';
|
||||
}
|
||||
else if(flags & FLAG_SPACE) {
|
||||
has_sgn = 1;
|
||||
sgn_ch = ' ';
|
||||
}
|
||||
// Fractional part
|
||||
frc = mnt;
|
||||
if(frc != 0) {
|
||||
while(!(frc & 0xf)) {
|
||||
frc >>= 4;
|
||||
}
|
||||
}
|
||||
u64 f = frc;
|
||||
do {
|
||||
int d = f % 16;
|
||||
f /= 16;
|
||||
*--frc_buf = fromdigit(d, flags & FLAG_UPPER);
|
||||
} while(f != 0);
|
||||
// Frac/whole digits
|
||||
if(flags & FLAG_PREC) {
|
||||
nfrcp = prec;
|
||||
}
|
||||
else {
|
||||
nfrcp = nfrc;
|
||||
}
|
||||
// Decimal point
|
||||
has_point = 1;
|
||||
if(nfrc == 0 && !(flags & FLAG_HASH)) {
|
||||
has_point = 0;
|
||||
}
|
||||
// Exponent
|
||||
i64 e = exp;
|
||||
if(e < 0) {
|
||||
exp_sgn_ch = '-';
|
||||
e = -e;
|
||||
}
|
||||
else {
|
||||
exp_sgn_ch = '+';
|
||||
}
|
||||
do {
|
||||
*--exp_buf = (e%10) + '0';
|
||||
e /= 10;
|
||||
nexp ++;
|
||||
} while(e != 0);
|
||||
}
|
||||
// Figure out the width of a float
|
||||
int nfloat = has_sgn + 2 + 1 + has_point + nfrcp + 1 + 1 + nexp;
|
||||
int pad = MIN(0, width - nfloat);
|
||||
print_lwidth_spaces(flags, pad);
|
||||
if(has_sgn) {
|
||||
out(sgn_ch);
|
||||
}
|
||||
out('0');
|
||||
out((flags & FLAG_UPPER)? 'X' : 'x');
|
||||
print_lwidth_zeros(flags, pad);
|
||||
out((flt_exp == 0) ? '0' : '1');
|
||||
if(has_point) {
|
||||
out('.');
|
||||
}
|
||||
int i;
|
||||
for(i = 0; i != nfrcp; ++i) {
|
||||
if(i < nfrc) {
|
||||
out(frc_buf[i]);
|
||||
}
|
||||
else {
|
||||
out('0');
|
||||
}
|
||||
}
|
||||
out((flags & FLAG_UPPER)? 'P' : 'p');
|
||||
out(exp_sgn_ch);
|
||||
for(int i = 0; i != nexp; ++i) {
|
||||
out(exp_buf[i]);
|
||||
}
|
||||
print_rwidth(flags, pad);
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline int pfx(_dtoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
decfloat_t value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
) {
|
||||
int64_t exp = value.exponent;
|
||||
int64_t mant = value.mantissa;
|
||||
// Figure out how many digits does mantissa take up (mant_digits_n)
|
||||
// and the number of digits after decimal point (prec)
|
||||
// and the number of digits before decimal point (while_digits_n)
|
||||
static ctype mant_buf[64] = {0};
|
||||
int whole_digits_n;
|
||||
int mant_digits_n = 0;
|
||||
{
|
||||
int64_t m = mant;
|
||||
do {
|
||||
mant_buf[mant_digits_n++] = m % 10 + '0';
|
||||
m /= 10;
|
||||
} while(m != 0);
|
||||
}
|
||||
if((flags & FLAG_PREC) == 0) {
|
||||
prec = 6;
|
||||
}
|
||||
whole_digits_n = mant_digits_n + exp;
|
||||
if(whole_digits_n <= 0) whole_digits_n = 1;
|
||||
// Figure out how many symbols decimal point takes up (0 or 1)
|
||||
int decimal_point_n = 1;
|
||||
if(prec == 0) {
|
||||
decimal_point_n = 0;
|
||||
if(flags & FLAG_HASH) {
|
||||
decimal_point_n = 1;
|
||||
}
|
||||
}
|
||||
// Figure out sign symbol and number of chars it takes up (0 or 1)
|
||||
int sign_n = 0;
|
||||
ctype sign_ch;
|
||||
if(value.sign) {
|
||||
sign_n = 1;
|
||||
sign_ch = '-';
|
||||
}
|
||||
else if(flags & FLAG_PLUS) {
|
||||
sign_n = 1;
|
||||
sign_ch = '+';
|
||||
}
|
||||
else if(flags & FLAG_SPACE) {
|
||||
sign_n = 1;
|
||||
sign_ch = ' ';
|
||||
}
|
||||
// Figure out the width of the number
|
||||
int significand_width = whole_digits_n + decimal_point_n + prec;
|
||||
int n_width = sign_n + significand_width;
|
||||
// Figure out width-padding required
|
||||
int pad = 0;
|
||||
if(width > n_width) pad = width - n_width;
|
||||
// Print width left-pad if it's made out of space
|
||||
if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
// Print sign if there
|
||||
if(sign_n)
|
||||
out(sign_ch);
|
||||
// Print width left-pad if it's made out of zero
|
||||
if(!(flags & FLAG_LEFT) && (flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out('0');
|
||||
}
|
||||
// Print whole part
|
||||
while(whole_digits_n--) {
|
||||
if(-exp >= mant_digits_n) {
|
||||
out('0');
|
||||
}
|
||||
else {
|
||||
out(mant_buf[--mant_digits_n]);
|
||||
}
|
||||
++exp;
|
||||
}
|
||||
// Print decimal point
|
||||
if(decimal_point_n) out('.');
|
||||
// Print fractional part that's made out of digits
|
||||
int prec_digs = prec < mant_digits_n ? prec : mant_digits_n;
|
||||
while(mant_digits_n < prec_digs) {
|
||||
if(-exp >= mant_digits_n) {
|
||||
out('0');
|
||||
}
|
||||
else {
|
||||
out(mant_buf[--mant_digits_n]);
|
||||
}
|
||||
++exp;
|
||||
}
|
||||
while(prec_digs-- > 0) {
|
||||
out('0');
|
||||
}
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline int pfx(_etoa)(
|
||||
int w,
|
||||
void *ctx,
|
||||
pfx(cbfn) cb,
|
||||
decfloat_t value,
|
||||
int prec,
|
||||
int width,
|
||||
int flags
|
||||
) {
|
||||
int64_t exp = value.exponent;
|
||||
int64_t mant = value.mantissa;
|
||||
// Figure out how many digits does mantissa take up (mant_digits_n)
|
||||
// and the number of digits after decimal point (prec)
|
||||
static ctype mant_buf[64] = {0};
|
||||
int mant_digits_n = 0;
|
||||
{
|
||||
int64_t m = mant;
|
||||
do {
|
||||
mant_buf[mant_digits_n++] = m % 10 + '0';
|
||||
m /= 10;
|
||||
} while(m != 0);
|
||||
}
|
||||
mant_digits_n -= 1;
|
||||
// Need to adjust exponent based on the amount of digits in the mantissa
|
||||
exp += mant_digits_n;
|
||||
if((flags & FLAG_PREC) == 0) {
|
||||
prec = 6;
|
||||
}
|
||||
// Figure out how many symbols decimal point takes up (0 or 1)
|
||||
int decimal_point_n = 1;
|
||||
if(prec == 0) {
|
||||
decimal_point_n = 0;
|
||||
if(flags & FLAG_HASH) {
|
||||
decimal_point_n = 1;
|
||||
}
|
||||
}
|
||||
// Figure out how many digits exponent take up and it's sign
|
||||
// also save exponent digits to a buffer starting from LSD
|
||||
static ctype exp_buf[64] = {0};
|
||||
int exp_digits_n = 0;
|
||||
ctype exp_sign;
|
||||
if(exp < 0) {
|
||||
exp_sign = '-';
|
||||
exp = -exp;
|
||||
}
|
||||
else {
|
||||
exp_sign = '+';
|
||||
}
|
||||
{
|
||||
int64_t e = exp;
|
||||
do {
|
||||
exp_buf[exp_digits_n++] = e % 10 + '0';
|
||||
e /= 10;
|
||||
} while(e != 0);
|
||||
}
|
||||
// Figure out sign symbol and number of chars it takes up (0 or 1)
|
||||
int sign_n = 0;
|
||||
ctype sign_ch;
|
||||
if(value.sign) {
|
||||
sign_n = 1;
|
||||
sign_ch = '-';
|
||||
}
|
||||
else if(flags & FLAG_PLUS) {
|
||||
sign_n = 1;
|
||||
sign_ch = '+';
|
||||
}
|
||||
else if(flags & FLAG_SPACE) {
|
||||
sign_n = 1;
|
||||
sign_ch = ' ';
|
||||
}
|
||||
// Figure out the width of the number
|
||||
int significand_width = 1 + decimal_point_n + prec;
|
||||
int exp_part_width = 2 /*e+-*/ + exp_digits_n;
|
||||
int n_width = sign_n + significand_width + exp_part_width;
|
||||
// Figure out width-padding required
|
||||
int pad = 0;
|
||||
if(width > n_width) pad = width - n_width;
|
||||
// Print width left-pad if it's made out of space
|
||||
if(!(flags & FLAG_LEFT) && !(flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
// Print sign if there
|
||||
if(sign_n)
|
||||
out(sign_ch);
|
||||
// Print width left-pad if it's made out of zero
|
||||
if(!(flags & FLAG_LEFT) && (flags & FLAG_ZERO)) while(pad-- > 0) {
|
||||
out('0');
|
||||
}
|
||||
// Print whole part
|
||||
out(mant_buf[mant_digits_n]);
|
||||
// Print decimal point
|
||||
if(decimal_point_n) out('.');
|
||||
// Print precision padding
|
||||
for(int i = mant_digits_n; i < prec; ++i) {
|
||||
out('0');
|
||||
}
|
||||
if(prec < mant_digits_n) mant_digits_n = prec;
|
||||
while(mant_digits_n--)
|
||||
out(mant_buf[mant_digits_n]);
|
||||
// Print the exponent part
|
||||
out((flags & FLAG_UPPER)? 'E' : 'e');
|
||||
out(exp_sign);
|
||||
while(exp_digits_n--)
|
||||
out(exp_buf[exp_digits_n]);
|
||||
// Print right-pad
|
||||
if(flags & FLAG_LEFT) while(pad-- > 0) {
|
||||
out(' ');
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
#undef out
|
150
src/fmt/ints.h
150
src/fmt/ints.h
|
@ -1,150 +0,0 @@
|
|||
|
||||
static inline u64 pfx(fmt_int_arg)(
|
||||
fmt_stream_t *stream,
|
||||
fmt_t *fmt,
|
||||
ctype const **strp,
|
||||
int *signp,
|
||||
int *basep,
|
||||
va_list va
|
||||
) {
|
||||
ctype const *str = *strp;
|
||||
// Get integer properties
|
||||
int base;
|
||||
int is_signed;
|
||||
switch(*str) {
|
||||
case 'o': base = 8; break;
|
||||
case 'b': base = 2; break;
|
||||
case 'x':
|
||||
case 'X': base = 16; break;
|
||||
default: base = 10;
|
||||
}
|
||||
switch(*str) {
|
||||
case 'd': is_signed = 1; break;
|
||||
case 'i': is_signed = 1; break;
|
||||
default: is_signed = 0;
|
||||
}
|
||||
if(*str == 'X') {
|
||||
fmt->flags |= FLAG_UPPER;
|
||||
}
|
||||
// Discard some flags
|
||||
if(base == 10) {
|
||||
fmt->flags &= ~FLAG_HASH;
|
||||
}
|
||||
if(!is_signed) {
|
||||
fmt->flags &= ~(FLAG_PLUS|FLAG_SPACE);
|
||||
}
|
||||
if(fmt->flags & FLAG_PREC) {
|
||||
fmt->flags &= ~FLAG_ZERO;
|
||||
}
|
||||
// Parse sign and abs value of an integer
|
||||
int sign = 0;
|
||||
u64 abs_value = 0;
|
||||
if(is_signed) {
|
||||
i64 num;
|
||||
if(fmt->flags & FLAG_LLONG) {
|
||||
num = (i64)va_arg(va, intll);
|
||||
}
|
||||
else if(fmt->flags & FLAG_LONG) {
|
||||
num = (i64)va_arg(va, intl);
|
||||
}
|
||||
else {
|
||||
num = (i64)va_arg(va, int);
|
||||
}
|
||||
// Handle overflow on '-'
|
||||
if(num == LLONG_MIN) {
|
||||
sign = 1;
|
||||
abs_value = (u64)LLONG_MAX + 1;
|
||||
}
|
||||
else {
|
||||
sign = num >= 0? 0 : 1;
|
||||
num = num >= 0? num : -num;
|
||||
abs_value = (u64)num;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(fmt->flags & FLAG_LLONG) {
|
||||
abs_value = (u64)va_arg(va, intull);
|
||||
}
|
||||
else if(fmt->flags & FLAG_LONG) {
|
||||
abs_value = (u64)va_arg(va, intul);
|
||||
}
|
||||
else {
|
||||
abs_value = (u64)va_arg(va, intu);
|
||||
}
|
||||
}
|
||||
++str;
|
||||
*strp = str;
|
||||
*basep = base;
|
||||
*signp = sign;
|
||||
return abs_value;
|
||||
}
|
||||
|
||||
static inline int pfx(fmt_int)(
|
||||
fmt_stream_t *stream,
|
||||
fmt_t fmt,
|
||||
int neg,
|
||||
u64 value,
|
||||
int base) {
|
||||
// We'll Write digits into buf in reverse and then call _out_rbuf
|
||||
int ndigits = 0;
|
||||
ctype digits[64] = {0};
|
||||
// Remove hash flag for 0 values
|
||||
if(value == 0) fmt.flags &= ~FLAG_HASH;
|
||||
// Write digits to buffer in reverse (starting from least significant)
|
||||
do {
|
||||
int d = (int)(value%base);
|
||||
value /= base;
|
||||
if(d < 10)
|
||||
d += '0';
|
||||
else if(fmt.flags & FLAG_UPPER)
|
||||
d += 'A' - 10;
|
||||
else d += 'a' - 10;
|
||||
digits[ndigits++] = d;
|
||||
} while(value);
|
||||
// Figure out the length of the prefix (part of number before digit stirng)
|
||||
int pref_len = 0;
|
||||
if(fmt.flags & FLAG_HASH) pref_len = base == 8? 1 : 2;
|
||||
else if(neg) pref_len = 1;
|
||||
else if(fmt.flags & FLAG_PLUS) pref_len = 1;
|
||||
else if(fmt.flags & FLAG_SPACE) pref_len = 1;
|
||||
if(ndigits > fmt.prec) fmt.prec = ndigits;
|
||||
int num_len = pref_len + fmt.prec;
|
||||
int pad_len = fmt.width - num_len;
|
||||
if(!(fmt.flags & FLAG_LEFT)) {
|
||||
ctype pad_ch = ' ';
|
||||
if(fmt.flags & FLAG_ZERO) pad_ch = '0';
|
||||
while(pad_len-- > 0) {
|
||||
stream_out(stream, pad_ch);
|
||||
}
|
||||
}
|
||||
// Print width left-pad if it's made out of space
|
||||
if(!(fmt.flags & FLAG_LEFT) && !(fmt.flags & FLAG_ZERO)) while(pad_len-- > 0) {
|
||||
stream_out(stream, ' ');
|
||||
}
|
||||
// Print prefix
|
||||
if(fmt.flags & FLAG_HASH) {
|
||||
stream_out(stream, '0');
|
||||
if(base == 16) { stream_out(stream, 'x'); }
|
||||
else if(base == 2) { stream_out(stream, 'b'); }
|
||||
}
|
||||
else if(neg) { stream_out(stream, '-'); }
|
||||
else if(fmt.flags & FLAG_PLUS) { stream_out(stream, '+'); }
|
||||
else if(fmt.flags & FLAG_SPACE) { stream_out(stream, ' '); }
|
||||
// Print width left-pad if it's made out of zero
|
||||
if(!(fmt.flags & FLAG_LEFT) && (fmt.flags & FLAG_ZERO)) while(pad_len-- > 0) {
|
||||
stream_out(stream, '0');
|
||||
}
|
||||
// Print zero-pad due to precision
|
||||
for(int i = ndigits; i < fmt.prec; ++i) {
|
||||
stream_out(stream, '0');
|
||||
}
|
||||
// Output buffer in reverse
|
||||
if(ndigits) while(ndigits--) {
|
||||
stream_out(stream, digits[ndigits]);
|
||||
}
|
||||
// Print right-pad
|
||||
if(fmt.flags & FLAG_LEFT) while(pad_len-- > 0) {
|
||||
stream_out(stream, ' ');
|
||||
}
|
||||
return stream->w;
|
||||
}
|
|
@ -18,12 +18,12 @@ static void _print_stack_trace() {
|
|||
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",
|
||||
" %d: %s\n",
|
||||
(int)(frames-i-1),
|
||||
//symbol->Address,
|
||||
symbol->Name
|
||||
);
|
||||
// printf(//" %u: 0x%"PRIx64" (%s)\n",
|
||||
// " %d: %s\n",
|
||||
// (int)(frames-i-1),
|
||||
// //symbol->Address,
|
||||
// symbol->Name
|
||||
// );
|
||||
}
|
||||
free(symbol);
|
||||
}
|
||||
|
@ -39,10 +39,10 @@ void _assert(
|
|||
// For GUI application we display the info into a messagebox
|
||||
char buf[1024];
|
||||
int i = 0;
|
||||
i += snprintf(buf+i, sizeof buf-i, "Assertion failed: %s\n", cond);
|
||||
i += snprintf(buf+i, sizeof buf-i, " Function: %s\n", func);
|
||||
i += snprintf(buf+i, sizeof buf-i, " File: %s\n", file);
|
||||
i += snprintf(buf+i, sizeof buf-i, " Line: %d\n", line);
|
||||
// i += snprintf(buf+i, sizeof buf-i, "Assertion failed: %s\n", cond);
|
||||
// i += snprintf(buf+i, sizeof buf-i, " Function: %s\n", func);
|
||||
// i += snprintf(buf+i, sizeof buf-i, " File: %s\n", file);
|
||||
// i += snprintf(buf+i, sizeof buf-i, " Line: %d\n", line);
|
||||
display_msg:
|
||||
int reaction = MessageBoxA(NULL, buf, "Assertion Failed", 0x00000032L);
|
||||
switch(reaction) {
|
||||
|
@ -53,11 +53,11 @@ display_msg:
|
|||
}
|
||||
else {
|
||||
// For console application we print the info to console
|
||||
printf("Assertion failed: %s\n", cond);
|
||||
printf(" Function: %s\n", func);
|
||||
printf(" File: %s\n", file);
|
||||
printf(" Line: %d\n", line);
|
||||
printf("Trace:\n");
|
||||
// printf("Assertion failed: %s\n", cond);
|
||||
// printf(" Function: %s\n", func);
|
||||
// printf(" File: %s\n", file);
|
||||
// printf(" Line: %d\n", line);
|
||||
// printf("Trace:\n");
|
||||
_print_stack_trace();
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ void __security_init_cookie() {
|
|||
|
||||
void __security_check_cookie(u64 retrieved) {
|
||||
if(__security_cookie != retrieved) {
|
||||
printf("Bro you've got a buffer overrun\n");
|
||||
// printf("Bro you've got a buffer overrun\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ _Noreturn void mainCRTStartup() {
|
|||
_setup_eh();
|
||||
_setup_heap();
|
||||
_setup_timer();
|
||||
_setup_io();
|
||||
_io_init();
|
||||
__security_init_cookie();
|
||||
|
||||
srand(0);
|
||||
|
@ -28,7 +28,7 @@ _Noreturn void mainCRTStartup() {
|
|||
_setup_eh();
|
||||
_setup_heap();
|
||||
_setup_timer();
|
||||
_setup_io();
|
||||
_io_init();
|
||||
__security_init_cookie();
|
||||
|
||||
srand(0);
|
||||
|
|
|
@ -34,7 +34,7 @@ _Noreturn void exit(int status) {
|
|||
while(atexit_func_count--) {
|
||||
atexit_funcs[atqexit_func_count]();
|
||||
}
|
||||
_close_io();
|
||||
_io_close();
|
||||
_Exit(status);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,17 @@
|
|||
|
||||
enum stream_width_t {
|
||||
STREAM_CHAR_UNSET,
|
||||
STREAM_CHAR_NARROW,
|
||||
STREAM_CHAR_WIDE,
|
||||
} typedef stream_width_t;
|
||||
enum FileMode {
|
||||
MODE_INPUT,
|
||||
MODE_OUTPUT,
|
||||
MODE_UPDATE,
|
||||
} typedef FileMode;
|
||||
|
||||
enum stream_io_mode_t {
|
||||
STREAM_INPUT,
|
||||
STREAM_OUTPUT,
|
||||
STREAM_UPDATE,
|
||||
} typedef stream_io_mode_t;
|
||||
enum FileType {
|
||||
FILE_BINARY,
|
||||
FILE_TEXT,
|
||||
} typedef FileType;
|
||||
|
||||
enum stream_bt_mode_t {
|
||||
STREAM_BINARY,
|
||||
STREAM_TEXT,
|
||||
} typedef stream_bt_mode_t;
|
||||
|
||||
struct stream_buffer_t typedef stream_buffer_t;
|
||||
struct stream_buffer_t {
|
||||
struct FileBuffer typedef FileBuffer;
|
||||
struct FileBuffer {
|
||||
int is_internal;
|
||||
int mode;
|
||||
size_t size;
|
||||
|
@ -27,14 +21,15 @@ struct stream_buffer_t {
|
|||
|
||||
struct FILE {
|
||||
HANDLE handle;
|
||||
stream_width_t char_width; // This technically needs not be stored
|
||||
mbstate_t mbstate;
|
||||
stream_buffer_t buffer;
|
||||
stream_io_mode_t io_mode;
|
||||
stream_bt_mode_t bt_mode;
|
||||
FileBuffer buffer;
|
||||
FileMode io_mode;
|
||||
FileType bt_mode;
|
||||
int eof;
|
||||
int err;
|
||||
mtx_t lock;
|
||||
bool temp;
|
||||
char *temp_name;
|
||||
FILE *prev;
|
||||
FILE *next;
|
||||
};
|
||||
|
@ -46,108 +41,121 @@ FILE *stderr;
|
|||
// We hold a linked list of all file streams in order to flush all the buffers
|
||||
// after the program terminates. It might be not a good idea to store all the
|
||||
// files into this linked list, but for now performance is not a concern.
|
||||
static FILE *streams_to_close = NULL;
|
||||
|
||||
static void close_list_add(FILE *stream) {
|
||||
if(streams_to_close != NULL) {
|
||||
streams_to_close->next = stream;
|
||||
stream->prev = streams_to_close;
|
||||
static FILE *_file_tracker = NULL;
|
||||
|
||||
static void _file_track(FILE *stream) {
|
||||
if(_file_tracker != NULL) {
|
||||
_file_tracker->next = stream;
|
||||
stream->prev = _file_tracker;
|
||||
stream->next = NULL;
|
||||
streams_to_close = stream;
|
||||
_file_tracker = stream;
|
||||
}
|
||||
else {
|
||||
streams_to_close = stream;
|
||||
_file_tracker = stream;
|
||||
stream->prev = NULL;
|
||||
stream->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_list_remove(FILE *stream) {
|
||||
static void _file_untrack(FILE *stream) {
|
||||
FILE *prev = stream->prev;
|
||||
FILE *next = stream->next;
|
||||
if(prev != NULL) prev->next = next;
|
||||
if(next != NULL) next->prev = prev;
|
||||
if(next == NULL) streams_to_close = prev;
|
||||
if(next == NULL) _file_tracker = prev;
|
||||
}
|
||||
|
||||
static inline void init_stream(
|
||||
FILE *stream,
|
||||
HANDLE handle,
|
||||
stream_io_mode_t io_mode,
|
||||
stream_bt_mode_t bt_mode
|
||||
) {
|
||||
// Multithreaded access
|
||||
|
||||
static inline void _file_lock_init(FILE *stream) {
|
||||
mtx_init(&stream->lock, mtx_recursive);
|
||||
}
|
||||
|
||||
static inline void _file_lock_destroy(FILE *stream) {
|
||||
mtx_destroy(&stream->lock);
|
||||
}
|
||||
|
||||
static inline void _file_lock(FILE *stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
}
|
||||
|
||||
static inline void _file_unlock(FILE *stream) {
|
||||
mtx_unlock(&stream->lock);
|
||||
}
|
||||
|
||||
// Managing file handles
|
||||
|
||||
static inline FILE *_file_create(HANDLE handle, FileMode io_mode, FileType bt_mode) {
|
||||
FILE *stream = calloc(1, sizeof(FILE));
|
||||
if(stream == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
stream->handle = handle;
|
||||
stream->char_width = STREAM_CHAR_UNSET;
|
||||
stream->mbstate = (mbstate_t){0};
|
||||
stream->buffer.mode = _IONBF;
|
||||
stream->io_mode = io_mode;
|
||||
stream->bt_mode = bt_mode;
|
||||
stream->eof = 0;
|
||||
stream->err = 0;
|
||||
}
|
||||
|
||||
static inline FILE *create_stream(
|
||||
HANDLE handle,
|
||||
stream_io_mode_t io_mode,
|
||||
stream_bt_mode_t bt_mode
|
||||
) {
|
||||
FILE *stream = malloc(sizeof(FILE));
|
||||
if(stream == NULL) return NULL;
|
||||
init_stream(stream, handle, io_mode, bt_mode);
|
||||
mtx_init(&stream->lock, mtx_recursive);
|
||||
close_list_add(stream);
|
||||
_file_lock_init(stream);
|
||||
_file_track(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
static inline void delete_stream(FILE *stream) {
|
||||
mtx_t lock = stream->lock;
|
||||
mtx_lock(&lock);
|
||||
static inline void _file_close(FILE *stream) {
|
||||
_file_lock(stream);
|
||||
CloseHandle(stream->handle);
|
||||
close_list_remove(stream);
|
||||
_file_untrack(stream);
|
||||
_file_unlock(stream);
|
||||
_file_lock_destroy(stream);
|
||||
free(stream);
|
||||
mtx_unlock(&lock);
|
||||
mtx_destroy(&lock);
|
||||
}
|
||||
|
||||
static void _setup_io() {
|
||||
static void _io_init() {
|
||||
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
HANDLE hstderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
stdout = create_stream(hstdout, STREAM_UPDATE, STREAM_TEXT);
|
||||
stderr = create_stream(hstderr, STREAM_UPDATE, STREAM_TEXT);
|
||||
stdin = create_stream(hstdin, STREAM_INPUT, STREAM_BINARY);
|
||||
stdout = _file_create(hstdout, MODE_UPDATE, FILE_TEXT);
|
||||
stderr = _file_create(hstderr, MODE_UPDATE, FILE_TEXT);
|
||||
stdin = _file_create(hstdin, MODE_INPUT, FILE_BINARY);
|
||||
|
||||
char *in_buf = calloc(BUFSIZ, sizeof(char));
|
||||
char *out_buf = calloc(BUFSIZ, sizeof(char));
|
||||
stdin->buffer = (stream_buffer_t){1, _IOLBF, BUFSIZ, in_buf};
|
||||
stdout->buffer = (stream_buffer_t){1, _IOLBF, BUFSIZ, out_buf};
|
||||
stderr->buffer = (stream_buffer_t){1, _IONBF, 0, NULL};
|
||||
stdin->buffer = (FileBuffer){1, _IOLBF, BUFSIZ, in_buf};
|
||||
stdout->buffer = (FileBuffer){1, _IOLBF, BUFSIZ, out_buf};
|
||||
stderr->buffer = (FileBuffer){1, _IONBF, 0, NULL};
|
||||
|
||||
SetConsoleCP(CP_UTF8); // maybe will work someday
|
||||
SetConsoleCP(CP_UTF8); // Maybe it will work someday
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
}
|
||||
|
||||
int win_parse_mode(
|
||||
char const *mode,
|
||||
stream_io_mode_t *pio_mode,
|
||||
stream_bt_mode_t *pbt_mode,
|
||||
DWORD *paccess,
|
||||
DWORD *pshare,
|
||||
DWORD *pdisp
|
||||
) {
|
||||
static void _io_close() {
|
||||
while(_file_tracker != NULL) {
|
||||
FILE *stream = _file_tracker;
|
||||
fclose(stream);
|
||||
}
|
||||
}
|
||||
|
||||
// File mode parsing
|
||||
|
||||
struct WindowsMode typedef WindowsMode;
|
||||
struct WindowsMode {
|
||||
DWORD access;
|
||||
DWORD share;
|
||||
DWORD disp;
|
||||
};
|
||||
|
||||
static int _mode_parse(char const *mode, FileMode *pio_mode, FileType *pbt_mode, WindowsMode *win_mode) {
|
||||
DWORD access = 0;
|
||||
DWORD share = 0;
|
||||
DWORD disp = 0;
|
||||
stream_io_mode_t io_mode = 0;
|
||||
stream_bt_mode_t bt_mode = 0;
|
||||
FileMode io_mode = 0;
|
||||
FileType bt_mode = 0;
|
||||
int flag_p = 0;
|
||||
int flag_b = 0;
|
||||
int flag_x = 0;
|
||||
switch(*mode++) {
|
||||
case 'r': io_mode = STREAM_INPUT; break;
|
||||
case 'w': io_mode = STREAM_OUTPUT; break;
|
||||
case 'a': io_mode = STREAM_UPDATE; break;
|
||||
case 'r': io_mode = MODE_INPUT; break;
|
||||
case 'w': io_mode = MODE_OUTPUT; break;
|
||||
case 'a': io_mode = MODE_UPDATE; break;
|
||||
default: return 0;
|
||||
}
|
||||
while(*mode) switch(*mode++) {
|
||||
|
@ -156,15 +164,14 @@ int win_parse_mode(
|
|||
case 'x': flag_x = 1; break;
|
||||
default: return 0;
|
||||
}
|
||||
bt_mode = flag_b? STREAM_BINARY : STREAM_TEXT;
|
||||
// Not sure about the sharing modes
|
||||
bt_mode = flag_b? FILE_BINARY : FILE_TEXT;
|
||||
switch(io_mode) {
|
||||
case STREAM_INPUT: {
|
||||
case MODE_INPUT: {
|
||||
access = GENERIC_READ | (flag_p? GENERIC_WRITE : 0);
|
||||
share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
disp = OPEN_EXISTING;
|
||||
} break;
|
||||
case STREAM_OUTPUT: {
|
||||
case MODE_OUTPUT: {
|
||||
access = GENERIC_WRITE | (flag_p? GENERIC_READ : 0);
|
||||
share = 0;
|
||||
disp = CREATE_ALWAYS;
|
||||
|
@ -172,37 +179,82 @@ int win_parse_mode(
|
|||
disp = CREATE_NEW;
|
||||
}
|
||||
} break;
|
||||
case STREAM_UPDATE: {
|
||||
case MODE_UPDATE: {
|
||||
access = GENERIC_READ | GENERIC_WRITE;
|
||||
share = 0;
|
||||
disp = OPEN_ALWAYS;
|
||||
} break;
|
||||
}
|
||||
*paccess = access;
|
||||
*pshare = share;
|
||||
*pdisp = disp;
|
||||
win_mode->access = access;
|
||||
win_mode->share = share;
|
||||
win_mode->disp = disp;
|
||||
*pio_mode = io_mode;
|
||||
*pbt_mode = bt_mode;
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *fopen(const char *restrict name, const char *restrict mode) {
|
||||
DWORD access;
|
||||
DWORD share;
|
||||
DWORD disp;
|
||||
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
||||
stream_io_mode_t io_mode;
|
||||
stream_bt_mode_t bt_mode;
|
||||
if(!win_parse_mode(mode, &io_mode, &bt_mode, &access, &share, &disp)) {
|
||||
// Operations on files
|
||||
|
||||
int remove(const char *filename) {
|
||||
BOOL ok = DeleteFileA(filename);
|
||||
return !ok;
|
||||
}
|
||||
|
||||
int rename(const char *old, const char *new) {
|
||||
BOOL ok = MoveFileA(old, new);
|
||||
return !ok;
|
||||
}
|
||||
|
||||
// Opening/closing files
|
||||
|
||||
char *tmpnam(char *s) {
|
||||
static char internal_buffer[L_tmpnam];
|
||||
char *buffer;
|
||||
if(s != NULL) {
|
||||
buffer = s;
|
||||
}
|
||||
else {
|
||||
buffer = &internal_buffer[0];
|
||||
}
|
||||
UINT num = GetTempFileNameA(".", ".t_", 0, buffer);
|
||||
if(num == 0) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
FILE *tmpfile(void) {
|
||||
char *file_name = malloc(L_tmpnam);
|
||||
UINT num = GetTempFileNameA(".", ".t_", 0, &file_name[0]);
|
||||
if(!num) {
|
||||
return NULL;
|
||||
}
|
||||
FILE *tmp_file = fopen(&file_name[0], "wb+");
|
||||
tmp_file->temp = true;
|
||||
tmp_file->temp_name = file_name;
|
||||
return tmp_file;
|
||||
}
|
||||
|
||||
FILE *fopen(const char *restrict name, const char *restrict mode) {
|
||||
WindowsMode win_mode;
|
||||
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
||||
FileMode io_mode;
|
||||
FileType bt_mode;
|
||||
if(!_mode_parse(mode, &io_mode, &bt_mode, &win_mode)) {
|
||||
return NULL;
|
||||
}
|
||||
DWORD access = win_mode.access;
|
||||
DWORD share = win_mode.share;
|
||||
DWORD disp = win_mode.disp;
|
||||
HANDLE handle = CreateFileA(name, access, share, NULL, disp, flags, NULL);
|
||||
if(handle == INVALID_HANDLE_VALUE) {
|
||||
return NULL;
|
||||
}
|
||||
FILE *stream = create_stream(handle, io_mode, bt_mode);
|
||||
FILE *stream = _file_create(handle, io_mode, bt_mode);
|
||||
void *buffer_data = malloc(BUFSIZ);
|
||||
stream->buffer = (stream_buffer_t) {1, _IOFBF, BUFSIZ, buffer_data};
|
||||
stream->buffer = (FileBuffer) {1, _IOFBF, BUFSIZ, buffer_data};
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
@ -211,15 +263,16 @@ FILE *freopen(const char *restrict name, const char *restrict mode, FILE *restri
|
|||
return NULL;
|
||||
}
|
||||
fflush(stream);
|
||||
DWORD access;
|
||||
DWORD share;
|
||||
DWORD disp;
|
||||
WindowsMode win_mode;
|
||||
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
||||
stream_io_mode_t io_mode;
|
||||
stream_bt_mode_t bt_mode;
|
||||
if(!win_parse_mode(mode, &io_mode, &bt_mode, &access, &share, &disp)) {
|
||||
FileMode io_mode;
|
||||
FileType bt_mode;
|
||||
if(!_mode_parse(mode, &io_mode, &bt_mode, &win_mode)) {
|
||||
return NULL;
|
||||
}
|
||||
DWORD access = win_mode.access;
|
||||
DWORD share = win_mode.share;
|
||||
DWORD disp = win_mode.disp;
|
||||
if(name == NULL) {
|
||||
HANDLE handle = ReOpenFile(stream->handle, access, share, flags);
|
||||
if(handle == INVALID_HANDLE_VALUE) {
|
||||
|
@ -232,15 +285,14 @@ FILE *freopen(const char *restrict name, const char *restrict mode, FILE *restri
|
|||
if(handle == INVALID_HANDLE_VALUE) {
|
||||
return NULL;
|
||||
}
|
||||
init_stream(stream, handle, io_mode, bt_mode);
|
||||
*stream = (FILE) {0};
|
||||
stream->handle = handle;
|
||||
stream->io_mode = io_mode;
|
||||
stream->bt_mode = bt_mode;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
FILE *tmpfile(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fclose(FILE *stream) {
|
||||
if(fflush(stream) == EOF) {
|
||||
return EOF;
|
||||
|
@ -251,17 +303,63 @@ int fclose(FILE *stream) {
|
|||
if(!CloseHandle(stream->handle)) {
|
||||
return EOF;
|
||||
}
|
||||
if(stream->temp) {
|
||||
BOOL err = DeleteFileA(stream->temp_name);
|
||||
free(stream->temp_name);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _close_io() {
|
||||
while(streams_to_close != NULL) {
|
||||
FILE *stream = streams_to_close;
|
||||
fflush(stream);
|
||||
delete_stream(stream);
|
||||
// Buffering
|
||||
|
||||
int setvbuf(FILE *restrict stream, char *restrict ptr, int mode, size_t size) {
|
||||
if(mode != _IOFBF && mode != _IOLBF && mode != _IONBF) {
|
||||
return 1;
|
||||
}
|
||||
_file_lock(stream);
|
||||
FileBuffer *buffer = &stream->buffer;
|
||||
buffer->mode = mode;
|
||||
if(ptr == NULL) {
|
||||
buffer->data = realloc(buffer->data, size);
|
||||
buffer->size = size;
|
||||
}
|
||||
else {
|
||||
buffer->data = ptr;
|
||||
buffer->size = size;
|
||||
}
|
||||
_file_unlock(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setbuf(FILE *restrict stream, char *restrict buf) {
|
||||
if(buf == NULL) {
|
||||
setvbuf(stream, NULL, _IONBF, 0);
|
||||
}
|
||||
else {
|
||||
setvbuf(stream, buf, _IOFBF, BUFSIZ);
|
||||
}
|
||||
}
|
||||
|
||||
int fflush(FILE *stream) {
|
||||
_file_lock(stream);
|
||||
int res = 0;
|
||||
FileBuffer *buffer = &stream->buffer;
|
||||
void *data = buffer->data;
|
||||
size_t size = buffer->written;
|
||||
DWORD bytes_written;
|
||||
BOOL ok = WriteFile(stream->handle, data, size, &bytes_written, NULL);
|
||||
if(!ok) {
|
||||
res = EOF;
|
||||
stream->eof = 1;
|
||||
}
|
||||
buffer->written = 0;
|
||||
_file_unlock(stream);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Errors
|
||||
|
||||
int feof(FILE *stream) {
|
||||
return stream->eof;
|
||||
}
|
||||
|
@ -276,57 +374,14 @@ void clearerr(FILE *stream) {
|
|||
}
|
||||
|
||||
void perror(char const *s) {
|
||||
printf("%s: %s\n", s, strerror(errno));
|
||||
// printf("%s: %s\n", s, strerror(errno));
|
||||
}
|
||||
|
||||
int setvbuf(FILE *restrict stream, char *restrict ptr, int mode, size_t size) {
|
||||
if(mode != _IOFBF && mode != _IOLBF && mode != _IONBF) {
|
||||
return 1;
|
||||
}
|
||||
mtx_lock(&stream->lock);
|
||||
stream_buffer_t *buffer = &stream->buffer;
|
||||
buffer->mode = mode;
|
||||
if(ptr == NULL) {
|
||||
buffer->data = realloc(buffer->data, size);
|
||||
buffer->size = size;
|
||||
}
|
||||
else {
|
||||
buffer->data = ptr;
|
||||
buffer->size = size;
|
||||
}
|
||||
mtx_unlock(&stream->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setbuf(FILE *restrict stream, char *restrict buf) {
|
||||
if(buf == NULL) {
|
||||
setvbuf(stream, NULL, _IONBF, 0);
|
||||
}
|
||||
else {
|
||||
setvbuf(stream, buf, _IOFBF, BUFSIZ);
|
||||
}
|
||||
}
|
||||
|
||||
int fflush(FILE *stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
int res = 0;
|
||||
stream_buffer_t *buffer = &stream->buffer;
|
||||
void *data = buffer->data;
|
||||
size_t size = buffer->written;
|
||||
DWORD bytes_written;
|
||||
BOOL ok = WriteFile(stream->handle, data, size, &bytes_written, NULL);
|
||||
if(!ok) {
|
||||
res = EOF;
|
||||
stream->eof = 1;
|
||||
}
|
||||
buffer->written = 0;
|
||||
mtx_unlock(&stream->lock);
|
||||
return res;
|
||||
}
|
||||
// File reads/writes
|
||||
|
||||
int fputc(int c, FILE *stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
stream_buffer_t *buffer = &stream->buffer;
|
||||
_file_lock(stream);
|
||||
FileBuffer *buffer = &stream->buffer;
|
||||
int res = c;
|
||||
if(buffer->mode == _IONBF) {
|
||||
unsigned char str[1] = {c};
|
||||
|
@ -335,7 +390,7 @@ int fputc(int c, FILE *stream) {
|
|||
if(!ok) {
|
||||
res = EOF;
|
||||
stream->err = 1;
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -349,19 +404,19 @@ int fputc(int c, FILE *stream) {
|
|||
}
|
||||
if(needs_flush) {
|
||||
if(fflush(stream) == EOF) {
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
cum:
|
||||
mtx_unlock(&stream->lock);
|
||||
end:
|
||||
_file_unlock(stream);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fgetc(FILE *stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
_file_lock(stream);
|
||||
int res = 0;
|
||||
stream_buffer_t *buffer = &stream->buffer;
|
||||
FileBuffer *buffer = &stream->buffer;
|
||||
int read_from_disk = 1;
|
||||
if(buffer->mode != _IONBF) {
|
||||
unsigned char *data = buffer->data;
|
||||
|
@ -377,95 +432,47 @@ int fgetc(FILE *stream) {
|
|||
if(bytes_read != 1) {
|
||||
res = EOF;
|
||||
stream->eof = 1;
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
if(!ok) {
|
||||
res = EOF;
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
res = buf[0];
|
||||
}
|
||||
cum:
|
||||
mtx_unlock(&stream->lock);
|
||||
end:
|
||||
_file_unlock(stream);
|
||||
return res;
|
||||
}
|
||||
|
||||
int ungetc(int c, FILE *stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
_file_lock(stream);
|
||||
int res;
|
||||
stream_buffer_t *buffer = &stream->buffer;
|
||||
FileBuffer *buffer = &stream->buffer;
|
||||
if(buffer->mode == _IONBF) {
|
||||
res = EOF;
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
else {
|
||||
if(c == EOF) {
|
||||
res = EOF;
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
if(buffer->written == buffer->size) {
|
||||
res = EOF;
|
||||
goto cum;
|
||||
goto end;
|
||||
}
|
||||
unsigned char *data = buffer->data;
|
||||
data[buffer->written++] = (unsigned char)c;
|
||||
res = c;
|
||||
}
|
||||
cum:
|
||||
mtx_unlock(&stream->lock);
|
||||
end:
|
||||
_file_unlock(stream);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fgetpos(FILE *restrict stream, fpos_t *restrict pos) {
|
||||
LONG pos_hi = 0;
|
||||
DWORD pos_lo = SetFilePointer(stream->handle, 0, &pos_hi, FILE_CURRENT);
|
||||
if(pos_lo == INVALID_SET_FILE_POINTER) {
|
||||
return 1;
|
||||
}
|
||||
int64_t offset = ((int64_t)pos_hi << 32) | (int64_t)pos_lo;
|
||||
pos->offset = offset;
|
||||
pos->mbstate = stream->mbstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fseek(FILE *stream, long int offset, int whence) {
|
||||
// Note(bumbread): the SEEK_SET, SEEK_CUR and SEEK_END are defined to match
|
||||
// Windows' constants, so no conversion is requierd between them.
|
||||
LONG pos_hi = 0;
|
||||
DWORD pos_lo = SetFilePointer(stream->handle, offset, &pos_hi, whence);
|
||||
if(pos_lo == INVALID_SET_FILE_POINTER) {
|
||||
return -1L;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsetpos(FILE *stream, const fpos_t *pos) {
|
||||
LONG pos_hi = pos->offset >> 32;
|
||||
LONG pos_lo = (LONG)(pos->offset & 0xffffffff);
|
||||
DWORD status = SetFilePointer(stream->handle, pos_lo, &pos_hi, FILE_BEGIN);
|
||||
if(status == INVALID_SET_FILE_POINTER) {
|
||||
return 1;
|
||||
}
|
||||
stream->mbstate = pos->mbstate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long int ftell(FILE *stream) {
|
||||
LONG pos_hi = 0;
|
||||
DWORD pos_lo = SetFilePointer(stream->handle, 0, &pos_hi, FILE_CURRENT);
|
||||
if(pos_lo == INVALID_SET_FILE_POINTER) {
|
||||
return -1L;
|
||||
}
|
||||
int64_t offset = ((int64_t)pos_hi << 32) | (int64_t)pos_lo;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void rewind(FILE *stream) {
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
size_t fwrite(void const *restrict buffer, size_t size, size_t count, FILE *restrict stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
_file_lock(stream);
|
||||
unsigned char const *bytes = (unsigned char const*)buffer;
|
||||
size_t num_written;
|
||||
for(num_written = 0; num_written < count; ++num_written) {
|
||||
|
@ -478,12 +485,12 @@ size_t fwrite(void const *restrict buffer, size_t size, size_t count, FILE *rest
|
|||
bytes += size;
|
||||
}
|
||||
end:
|
||||
mtx_unlock(&stream->lock);
|
||||
_file_unlock(stream);
|
||||
return num_written;
|
||||
}
|
||||
|
||||
size_t fread(void *restrict buffer, size_t size, size_t count, FILE *restrict stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
_file_lock(stream);
|
||||
unsigned char *bytes = (unsigned char *)buffer;
|
||||
size_t num_read;
|
||||
for(num_read = 0; num_read < count; ++num_read) {
|
||||
|
@ -497,7 +504,7 @@ size_t fread(void *restrict buffer, size_t size, size_t count, FILE *restrict st
|
|||
bytes += size;
|
||||
}
|
||||
end:
|
||||
mtx_unlock(&stream->lock);
|
||||
_file_unlock(stream);
|
||||
return num_read;
|
||||
}
|
||||
|
||||
|
@ -517,7 +524,7 @@ char *fgets(char *restrict str, int count, FILE *restrict stream) {
|
|||
str[0] = 0;
|
||||
return str;
|
||||
}
|
||||
mtx_lock(&stream->lock);
|
||||
_file_lock(stream);
|
||||
int i;
|
||||
for(i = 0; i < count-1; ++i) {
|
||||
int c = fgetc(stream);
|
||||
|
@ -535,12 +542,12 @@ char *fgets(char *restrict str, int count, FILE *restrict stream) {
|
|||
}
|
||||
}
|
||||
str[i] = 0;
|
||||
mtx_unlock(&stream->lock);
|
||||
_file_unlock(stream);
|
||||
return str;
|
||||
}
|
||||
|
||||
int fputs(char const *str, FILE *stream) {
|
||||
mtx_lock(&stream->lock);
|
||||
_file_lock(stream);
|
||||
int res = 0;
|
||||
while(*str) {
|
||||
int c = fputc(*str++, stream);
|
||||
|
@ -549,7 +556,7 @@ int fputs(char const *str, FILE *stream) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&stream->lock);
|
||||
_file_unlock(stream);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -562,3 +569,55 @@ int puts(char const *str) {
|
|||
char *gets(char *str) {
|
||||
return fgets(str, 0x7fffffff, stdin);
|
||||
}
|
||||
|
||||
// File positioning:
|
||||
|
||||
int fgetpos(FILE *restrict stream, fpos_t *restrict pos) {
|
||||
LONG pos_hi = 0;
|
||||
DWORD pos_lo = SetFilePointer(stream->handle, 0, &pos_hi, FILE_CURRENT);
|
||||
if(pos_lo == INVALID_SET_FILE_POINTER) {
|
||||
return 1;
|
||||
}
|
||||
int64_t offset = ((int64_t)pos_hi << 32) | (int64_t)pos_lo;
|
||||
pos->offset = offset;
|
||||
pos->mbstate.leftover = stream->mbstate.leftover;
|
||||
pos->mbstate.high_surrogate = stream->mbstate.high_surrogate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fseek(FILE *stream, long int offset, int whence) {
|
||||
// Note(bumbread): the SEEK_SET, SEEK_CUR and SEEK_END are defined to match
|
||||
// the Windows constants, so no conversion is requierd between them.
|
||||
LONG pos_hi = 0;
|
||||
DWORD pos_lo = SetFilePointer(stream->handle, offset, &pos_hi, whence);
|
||||
if(pos_lo == INVALID_SET_FILE_POINTER) {
|
||||
return -1L;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsetpos(FILE *stream, const fpos_t *pos) {
|
||||
LONG pos_hi = pos->offset >> 32;
|
||||
LONG pos_lo = (LONG)(pos->offset & 0xffffffff);
|
||||
DWORD status = SetFilePointer(stream->handle, pos_lo, &pos_hi, FILE_BEGIN);
|
||||
if(status == INVALID_SET_FILE_POINTER) {
|
||||
return 1;
|
||||
}
|
||||
stream->mbstate.leftover = pos->mbstate.leftover;
|
||||
stream->mbstate.high_surrogate = pos->mbstate.high_surrogate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long int ftell(FILE *stream) {
|
||||
LONG pos_hi = 0;
|
||||
DWORD pos_lo = SetFilePointer(stream->handle, 0, &pos_hi, FILE_CURRENT);
|
||||
if(pos_lo == INVALID_SET_FILE_POINTER) {
|
||||
return -1L;
|
||||
}
|
||||
int64_t offset = ((int64_t)pos_hi << 32) | (int64_t)pos_lo;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void rewind(FILE *stream) {
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
}
|
|
@ -10,5 +10,4 @@
|
|||
static void _setup_timer(void);
|
||||
static void _setup_eh();
|
||||
static void _setup_heap();
|
||||
static void _setup_io();
|
||||
static void _close_io();
|
||||
static void _io_close();
|
|
@ -2,29 +2,10 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
int main(void)
|
||||
{
|
||||
// prepare a file holding 4 values of type char
|
||||
enum {SIZE = 4};
|
||||
FILE* fp = fopen("test.bin", "wb");
|
||||
assert(fp);
|
||||
fputc('a', fp);
|
||||
fputc('b', fp);
|
||||
fputc('d', fp);
|
||||
fputc('c', fp);
|
||||
fclose(fp);
|
||||
|
||||
// demo using fsetpos to return to the beginning of a file
|
||||
fp = fopen("test.bin", "rb");
|
||||
fpos_t pos;
|
||||
int c = fgetc(fp);
|
||||
printf("First value in the file: %c\n", c);
|
||||
fgetpos(fp, &pos);
|
||||
c = fgetc(fp);
|
||||
printf("Second value in the file: %c\n", c);
|
||||
fsetpos(fp,&pos);
|
||||
c = fgetc(fp);
|
||||
printf("Second value in the file again: %c\n", c);
|
||||
fclose(fp);
|
||||
int main(void) {
|
||||
FILE *tmp = tmpfile();
|
||||
fputc('a', tmp);
|
||||
fclose(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue