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>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if !defined(NULL)
|
// 7.23.3 p.2: Header version
|
||||||
#define NULL ((void *)0)
|
#define __STDC_VERSION_STDIO_H__ 202311L
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// 7.23.3 p.3: Types
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
typedef unsigned long long size_t;
|
typedef unsigned long long size_t;
|
||||||
#else
|
#else
|
||||||
typedef unsigned long size_t;
|
typedef unsigned long size_t;
|
||||||
#endif
|
#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 FILE FILE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned long long offset;
|
unsigned long long offset;
|
||||||
mbstate_t mbstate;
|
union {
|
||||||
|
unsigned short leftover;
|
||||||
|
unsigned short high_surrogate;
|
||||||
|
} mbstate;
|
||||||
} fpos_t;
|
} fpos_t;
|
||||||
|
|
||||||
|
// 7.23.3 p.4: Macros
|
||||||
#if !defined(NULL)
|
#if !defined(NULL)
|
||||||
#define NULL ((void *)0)
|
#define NULL ((void *)0)
|
||||||
#endif
|
#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 _IONBF 0
|
||||||
#define _IOFBF 1
|
#define _IOFBF 1
|
||||||
#define _IOLBF 2
|
#define _IOLBF 2
|
||||||
#define BUFSIZ 512
|
#define BUFSIZ 512
|
||||||
#define EOF (-1)
|
#define EOF (-1)
|
||||||
#define FOPEN_MAX 1024
|
#define FOPEN_MAX 32
|
||||||
|
|
||||||
#ifdef _os_win
|
#ifdef _os_win
|
||||||
#define FILENAME_MAX 260
|
#define FILENAME_MAX 260
|
||||||
|
@ -60,6 +41,8 @@ typedef struct {
|
||||||
#define FILENAME_MAX 4096
|
#define FILENAME_MAX 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define _PRINTF_NAN_LEN_MAX 20
|
||||||
|
|
||||||
#define L_tmpnam FILENAME_MAX
|
#define L_tmpnam FILENAME_MAX
|
||||||
|
|
||||||
#define SEEK_SET 0
|
#define SEEK_SET 0
|
||||||
|
@ -72,17 +55,21 @@ typedef struct {
|
||||||
#define TMP_MAX INT_MAX
|
#define TMP_MAX INT_MAX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern FILE *stdout;
|
#define stdout _internal_stdout
|
||||||
extern FILE *stderr;
|
#define stderr _internal_stderr
|
||||||
extern FILE *stdin;
|
#define stdin _internal_stdin
|
||||||
|
|
||||||
|
extern FILE *_internal_stdout;
|
||||||
|
extern FILE *_internal_stderr;
|
||||||
|
extern FILE *_internal_stdin;
|
||||||
|
|
||||||
// File manipulation
|
// File manipulation
|
||||||
int remove(const char *filename);
|
int remove(const char *filename);
|
||||||
int rename(const char *oldname, const char *newname);
|
int rename(const char *oldname, const char *newname);
|
||||||
FILE *tmpfile(void);
|
|
||||||
char *tmpnam(char *s);
|
|
||||||
|
|
||||||
// Opening and closing files
|
// Opening and closing files
|
||||||
|
char *tmpnam(char *s);
|
||||||
|
FILE *tmpfile(void);
|
||||||
FILE *fopen (const char *restrict filename, const char *restrict mode);
|
FILE *fopen (const char *restrict filename, const char *restrict mode);
|
||||||
FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream);
|
FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream);
|
||||||
int fclose (FILE *stream);
|
int fclose (FILE *stream);
|
||||||
|
|
115
inc/threads.h
115
inc/threads.h
|
@ -1,47 +1,59 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define thread_local _Thread_local
|
// 7.28.1 p.3: Macros
|
||||||
|
|
||||||
#define ONCE_FLAG_INIT 1
|
#define ONCE_FLAG_INIT 1
|
||||||
#define TSS_DTOR_ITERATIONS 32
|
#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)
|
#if !defined(_timespec_defined)
|
||||||
#define _timespec_defined
|
#define _timespec_defined
|
||||||
typedef unsigned long long time_t;
|
typedef unsigned long long time_t;
|
||||||
struct timespec {
|
struct timespec {
|
||||||
time_t tv_sec;
|
time_t tv_sec;
|
||||||
long tv_nsec;
|
long tv_nsec;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO(bumbread): move this out to platform-specific folder
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
typedef struct cnd_t {
|
typedef struct cnd_t {
|
||||||
int idk_yet;
|
int idk_yet;
|
||||||
} cnd_t;
|
} cnd_t;
|
||||||
|
|
||||||
typedef struct thrd_t {
|
typedef struct thrd_t {
|
||||||
void* handle;
|
void* handle;
|
||||||
} thrd_t;
|
} thrd_t;
|
||||||
|
|
||||||
typedef struct tss_t {
|
typedef struct tss_t {
|
||||||
int idk_yet;
|
int idk_yet;
|
||||||
} tss_t;
|
} tss_t;
|
||||||
|
|
||||||
typedef struct mtx_t {
|
typedef struct mtx_t {
|
||||||
int type;
|
int type;
|
||||||
// Done to handle recursive mutexes
|
// Done to handle recursive mutexes
|
||||||
unsigned long recursion;
|
unsigned long recursion;
|
||||||
unsigned long owner;
|
unsigned long owner;
|
||||||
_Atomic(int) counter;
|
_Atomic(int) counter;
|
||||||
void* semaphore;
|
void* semaphore;
|
||||||
} mtx_t;
|
} mtx_t;
|
||||||
#else
|
#else
|
||||||
#error "Not implemented"
|
#error "Not implemented"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef void(*tss_dtor_t) (void*);
|
typedef void(*tss_dtor_t) (void*);
|
||||||
typedef int (*thrd_start_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 {
|
enum {
|
||||||
mtx_plain = 0,
|
mtx_plain = 0,
|
||||||
|
@ -57,29 +69,7 @@ enum {
|
||||||
thrd_nomem
|
thrd_nomem
|
||||||
};
|
};
|
||||||
|
|
||||||
void call_once(once_flag *flag, void (*func)(void));
|
// Thread 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
|
|
||||||
);
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
thrd_t thrd_current(void);
|
thrd_t thrd_current(void);
|
||||||
int thrd_create (thrd_t *thr, thrd_start_t func, void *arg);
|
int thrd_create (thrd_t *thr, thrd_start_t func, void *arg);
|
||||||
int thrd_detach (thrd_t thr);
|
int thrd_detach (thrd_t thr);
|
||||||
|
@ -92,7 +82,34 @@ int thrd_sleep(
|
||||||
);
|
);
|
||||||
_Noreturn void thrd_exit(int res);
|
_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);
|
int tss_create(tss_t *key, tss_dtor_t dtor);
|
||||||
void tss_delete(tss_t key);
|
void tss_delete(tss_t key);
|
||||||
void *tss_get (tss_t key);
|
void *tss_get (tss_t key);
|
||||||
int tss_set (tss_t key, void *val);
|
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/strpfx.c"
|
||||||
#include "conv/int.c"
|
#include "conv/int.c"
|
||||||
#include "conv/float.c"
|
#include "conv/float.c"
|
||||||
#include "fmt/fmt.c"
|
|
||||||
#include "stdlib/algorithm.c"
|
#include "stdlib/algorithm.c"
|
||||||
#include "stdlib/multibyte.c"
|
#include "stdlib/multibyte.c"
|
||||||
#include "stdlib/random.c"
|
#include "stdlib/random.c"
|
||||||
|
@ -69,11 +68,11 @@
|
||||||
#include "os_win/cookie.c"
|
#include "os_win/cookie.c"
|
||||||
#include "os_win/assert.c"
|
#include "os_win/assert.c"
|
||||||
#include "os_win/cmdline.c"
|
#include "os_win/cmdline.c"
|
||||||
#include "os_win/entry.c"
|
|
||||||
#include "os_win/environment.c"
|
#include "os_win/environment.c"
|
||||||
#include "os_win/heap.c"
|
#include "os_win/heap.c"
|
||||||
#include "os_win/signal.c"
|
#include "os_win/signal.c"
|
||||||
#include "os_win/stdio.c"
|
#include "os_win/file.c"
|
||||||
#include "os_win/threads.c"
|
#include "os_win/thread.c"
|
||||||
#include "os_win/time.c"
|
#include "os_win/time.c"
|
||||||
|
#include "os_win/entry.c"
|
||||||
#endif
|
#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);
|
SymFromAddr(process, (DWORD64)stack[i], 0, symbol);
|
||||||
// if(strcmp(symbol->Name, "BaseThreadInitThunk") == 0) break;
|
// if(strcmp(symbol->Name, "BaseThreadInitThunk") == 0) break;
|
||||||
// if(strcmp(symbol->Name, "mainCRTStartup") == 0) break;
|
// if(strcmp(symbol->Name, "mainCRTStartup") == 0) break;
|
||||||
printf(//" %u: 0x%"PRIx64" (%s)\n",
|
// printf(//" %u: 0x%"PRIx64" (%s)\n",
|
||||||
" %d: %s\n",
|
// " %d: %s\n",
|
||||||
(int)(frames-i-1),
|
// (int)(frames-i-1),
|
||||||
//symbol->Address,
|
// //symbol->Address,
|
||||||
symbol->Name
|
// symbol->Name
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
free(symbol);
|
free(symbol);
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,10 @@ void _assert(
|
||||||
// For GUI application we display the info into a messagebox
|
// For GUI application we display the info into a messagebox
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
i += snprintf(buf+i, sizeof buf-i, "Assertion failed: %s\n", cond);
|
// 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, " Function: %s\n", func);
|
||||||
i += snprintf(buf+i, sizeof buf-i, " File: %s\n", file);
|
// 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, " Line: %d\n", line);
|
||||||
display_msg:
|
display_msg:
|
||||||
int reaction = MessageBoxA(NULL, buf, "Assertion Failed", 0x00000032L);
|
int reaction = MessageBoxA(NULL, buf, "Assertion Failed", 0x00000032L);
|
||||||
switch(reaction) {
|
switch(reaction) {
|
||||||
|
@ -53,11 +53,11 @@ display_msg:
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// For console application we print the info to console
|
// For console application we print the info to console
|
||||||
printf("Assertion failed: %s\n", cond);
|
// printf("Assertion failed: %s\n", cond);
|
||||||
printf(" Function: %s\n", func);
|
// printf(" Function: %s\n", func);
|
||||||
printf(" File: %s\n", file);
|
// printf(" File: %s\n", file);
|
||||||
printf(" Line: %d\n", line);
|
// printf(" Line: %d\n", line);
|
||||||
printf("Trace:\n");
|
// printf("Trace:\n");
|
||||||
_print_stack_trace();
|
_print_stack_trace();
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ void __security_init_cookie() {
|
||||||
|
|
||||||
void __security_check_cookie(u64 retrieved) {
|
void __security_check_cookie(u64 retrieved) {
|
||||||
if(__security_cookie != retrieved) {
|
if(__security_cookie != retrieved) {
|
||||||
printf("Bro you've got a buffer overrun\n");
|
// printf("Bro you've got a buffer overrun\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ _Noreturn void mainCRTStartup() {
|
||||||
_setup_eh();
|
_setup_eh();
|
||||||
_setup_heap();
|
_setup_heap();
|
||||||
_setup_timer();
|
_setup_timer();
|
||||||
_setup_io();
|
_io_init();
|
||||||
__security_init_cookie();
|
__security_init_cookie();
|
||||||
|
|
||||||
srand(0);
|
srand(0);
|
||||||
|
@ -28,7 +28,7 @@ _Noreturn void mainCRTStartup() {
|
||||||
_setup_eh();
|
_setup_eh();
|
||||||
_setup_heap();
|
_setup_heap();
|
||||||
_setup_timer();
|
_setup_timer();
|
||||||
_setup_io();
|
_io_init();
|
||||||
__security_init_cookie();
|
__security_init_cookie();
|
||||||
|
|
||||||
srand(0);
|
srand(0);
|
||||||
|
|
|
@ -34,7 +34,7 @@ _Noreturn void exit(int status) {
|
||||||
while(atexit_func_count--) {
|
while(atexit_func_count--) {
|
||||||
atexit_funcs[atqexit_func_count]();
|
atexit_funcs[atqexit_func_count]();
|
||||||
}
|
}
|
||||||
_close_io();
|
_io_close();
|
||||||
_Exit(status);
|
_Exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1,37 @@
|
||||||
|
|
||||||
enum stream_width_t {
|
enum FileMode {
|
||||||
STREAM_CHAR_UNSET,
|
MODE_INPUT,
|
||||||
STREAM_CHAR_NARROW,
|
MODE_OUTPUT,
|
||||||
STREAM_CHAR_WIDE,
|
MODE_UPDATE,
|
||||||
} typedef stream_width_t;
|
} typedef FileMode;
|
||||||
|
|
||||||
enum stream_io_mode_t {
|
enum FileType {
|
||||||
STREAM_INPUT,
|
FILE_BINARY,
|
||||||
STREAM_OUTPUT,
|
FILE_TEXT,
|
||||||
STREAM_UPDATE,
|
} typedef FileType;
|
||||||
} typedef stream_io_mode_t;
|
|
||||||
|
|
||||||
enum stream_bt_mode_t {
|
struct FileBuffer typedef FileBuffer;
|
||||||
STREAM_BINARY,
|
struct FileBuffer {
|
||||||
STREAM_TEXT,
|
int is_internal;
|
||||||
} typedef stream_bt_mode_t;
|
int mode;
|
||||||
|
|
||||||
struct stream_buffer_t typedef stream_buffer_t;
|
|
||||||
struct stream_buffer_t {
|
|
||||||
int is_internal;
|
|
||||||
int mode;
|
|
||||||
size_t size;
|
size_t size;
|
||||||
void *data;
|
void *data;
|
||||||
size_t written;
|
size_t written;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FILE {
|
struct FILE {
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
stream_width_t char_width; // This technically needs not be stored
|
mbstate_t mbstate;
|
||||||
mbstate_t mbstate;
|
FileBuffer buffer;
|
||||||
stream_buffer_t buffer;
|
FileMode io_mode;
|
||||||
stream_io_mode_t io_mode;
|
FileType bt_mode;
|
||||||
stream_bt_mode_t bt_mode;
|
int eof;
|
||||||
int eof;
|
int err;
|
||||||
int err;
|
mtx_t lock;
|
||||||
mtx_t lock;
|
bool temp;
|
||||||
FILE *prev;
|
char *temp_name;
|
||||||
FILE *next;
|
FILE *prev;
|
||||||
|
FILE *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
FILE *stdout;
|
FILE *stdout;
|
||||||
|
@ -46,108 +41,121 @@ FILE *stderr;
|
||||||
// We hold a linked list of all file streams in order to flush all the buffers
|
// 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
|
// 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.
|
// 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) {
|
static FILE *_file_tracker = NULL;
|
||||||
if(streams_to_close != NULL) {
|
|
||||||
streams_to_close->next = stream;
|
static void _file_track(FILE *stream) {
|
||||||
stream->prev = streams_to_close;
|
if(_file_tracker != NULL) {
|
||||||
|
_file_tracker->next = stream;
|
||||||
|
stream->prev = _file_tracker;
|
||||||
stream->next = NULL;
|
stream->next = NULL;
|
||||||
streams_to_close = stream;
|
_file_tracker = stream;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
streams_to_close = stream;
|
_file_tracker = stream;
|
||||||
stream->prev = NULL;
|
stream->prev = NULL;
|
||||||
stream->next = NULL;
|
stream->next = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_list_remove(FILE *stream) {
|
static void _file_untrack(FILE *stream) {
|
||||||
FILE *prev = stream->prev;
|
FILE *prev = stream->prev;
|
||||||
FILE *next = stream->next;
|
FILE *next = stream->next;
|
||||||
if(prev != NULL) prev->next = next;
|
if(prev != NULL) prev->next = next;
|
||||||
if(next != NULL) next->prev = prev;
|
if(next != NULL) next->prev = prev;
|
||||||
if(next == NULL) streams_to_close = prev;
|
if(next == NULL) _file_tracker = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void init_stream(
|
// Multithreaded access
|
||||||
FILE *stream,
|
|
||||||
HANDLE handle,
|
static inline void _file_lock_init(FILE *stream) {
|
||||||
stream_io_mode_t io_mode,
|
mtx_init(&stream->lock, mtx_recursive);
|
||||||
stream_bt_mode_t bt_mode
|
}
|
||||||
) {
|
|
||||||
|
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->handle = handle;
|
||||||
stream->char_width = STREAM_CHAR_UNSET;
|
|
||||||
stream->mbstate = (mbstate_t){0};
|
|
||||||
stream->buffer.mode = _IONBF;
|
|
||||||
stream->io_mode = io_mode;
|
stream->io_mode = io_mode;
|
||||||
stream->bt_mode = bt_mode;
|
stream->bt_mode = bt_mode;
|
||||||
stream->eof = 0;
|
_file_lock_init(stream);
|
||||||
stream->err = 0;
|
_file_track(stream);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void delete_stream(FILE *stream) {
|
static inline void _file_close(FILE *stream) {
|
||||||
mtx_t lock = stream->lock;
|
_file_lock(stream);
|
||||||
mtx_lock(&lock);
|
|
||||||
CloseHandle(stream->handle);
|
CloseHandle(stream->handle);
|
||||||
close_list_remove(stream);
|
_file_untrack(stream);
|
||||||
|
_file_unlock(stream);
|
||||||
|
_file_lock_destroy(stream);
|
||||||
free(stream);
|
free(stream);
|
||||||
mtx_unlock(&lock);
|
|
||||||
mtx_destroy(&lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _setup_io() {
|
static void _io_init() {
|
||||||
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
HANDLE hstderr = GetStdHandle(STD_ERROR_HANDLE);
|
HANDLE hstderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
|
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
|
||||||
stdout = create_stream(hstdout, STREAM_UPDATE, STREAM_TEXT);
|
stdout = _file_create(hstdout, MODE_UPDATE, FILE_TEXT);
|
||||||
stderr = create_stream(hstderr, STREAM_UPDATE, STREAM_TEXT);
|
stderr = _file_create(hstderr, MODE_UPDATE, FILE_TEXT);
|
||||||
stdin = create_stream(hstdin, STREAM_INPUT, STREAM_BINARY);
|
stdin = _file_create(hstdin, MODE_INPUT, FILE_BINARY);
|
||||||
|
|
||||||
char *in_buf = calloc(BUFSIZ, sizeof(char));
|
char *in_buf = calloc(BUFSIZ, sizeof(char));
|
||||||
char *out_buf = calloc(BUFSIZ, sizeof(char));
|
char *out_buf = calloc(BUFSIZ, sizeof(char));
|
||||||
stdin->buffer = (stream_buffer_t){1, _IOLBF, BUFSIZ, in_buf};
|
stdin->buffer = (FileBuffer){1, _IOLBF, BUFSIZ, in_buf};
|
||||||
stdout->buffer = (stream_buffer_t){1, _IOLBF, BUFSIZ, out_buf};
|
stdout->buffer = (FileBuffer){1, _IOLBF, BUFSIZ, out_buf};
|
||||||
stderr->buffer = (stream_buffer_t){1, _IONBF, 0, NULL};
|
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);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
int win_parse_mode(
|
static void _io_close() {
|
||||||
char const *mode,
|
while(_file_tracker != NULL) {
|
||||||
stream_io_mode_t *pio_mode,
|
FILE *stream = _file_tracker;
|
||||||
stream_bt_mode_t *pbt_mode,
|
fclose(stream);
|
||||||
DWORD *paccess,
|
}
|
||||||
DWORD *pshare,
|
}
|
||||||
DWORD *pdisp
|
|
||||||
) {
|
// 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 access = 0;
|
||||||
DWORD share = 0;
|
DWORD share = 0;
|
||||||
DWORD disp = 0;
|
DWORD disp = 0;
|
||||||
stream_io_mode_t io_mode = 0;
|
FileMode io_mode = 0;
|
||||||
stream_bt_mode_t bt_mode = 0;
|
FileType bt_mode = 0;
|
||||||
int flag_p = 0;
|
int flag_p = 0;
|
||||||
int flag_b = 0;
|
int flag_b = 0;
|
||||||
int flag_x = 0;
|
int flag_x = 0;
|
||||||
switch(*mode++) {
|
switch(*mode++) {
|
||||||
case 'r': io_mode = STREAM_INPUT; break;
|
case 'r': io_mode = MODE_INPUT; break;
|
||||||
case 'w': io_mode = STREAM_OUTPUT; break;
|
case 'w': io_mode = MODE_OUTPUT; break;
|
||||||
case 'a': io_mode = STREAM_UPDATE; break;
|
case 'a': io_mode = MODE_UPDATE; break;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
while(*mode) switch(*mode++) {
|
while(*mode) switch(*mode++) {
|
||||||
|
@ -156,15 +164,14 @@ int win_parse_mode(
|
||||||
case 'x': flag_x = 1; break;
|
case 'x': flag_x = 1; break;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
bt_mode = flag_b? STREAM_BINARY : STREAM_TEXT;
|
bt_mode = flag_b? FILE_BINARY : FILE_TEXT;
|
||||||
// Not sure about the sharing modes
|
|
||||||
switch(io_mode) {
|
switch(io_mode) {
|
||||||
case STREAM_INPUT: {
|
case MODE_INPUT: {
|
||||||
access = GENERIC_READ | (flag_p? GENERIC_WRITE : 0);
|
access = GENERIC_READ | (flag_p? GENERIC_WRITE : 0);
|
||||||
share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||||
disp = OPEN_EXISTING;
|
disp = OPEN_EXISTING;
|
||||||
} break;
|
} break;
|
||||||
case STREAM_OUTPUT: {
|
case MODE_OUTPUT: {
|
||||||
access = GENERIC_WRITE | (flag_p? GENERIC_READ : 0);
|
access = GENERIC_WRITE | (flag_p? GENERIC_READ : 0);
|
||||||
share = 0;
|
share = 0;
|
||||||
disp = CREATE_ALWAYS;
|
disp = CREATE_ALWAYS;
|
||||||
|
@ -172,37 +179,82 @@ int win_parse_mode(
|
||||||
disp = CREATE_NEW;
|
disp = CREATE_NEW;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case STREAM_UPDATE: {
|
case MODE_UPDATE: {
|
||||||
access = GENERIC_READ | GENERIC_WRITE;
|
access = GENERIC_READ | GENERIC_WRITE;
|
||||||
share = 0;
|
share = 0;
|
||||||
disp = OPEN_ALWAYS;
|
disp = OPEN_ALWAYS;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
*paccess = access;
|
win_mode->access = access;
|
||||||
*pshare = share;
|
win_mode->share = share;
|
||||||
*pdisp = disp;
|
win_mode->disp = disp;
|
||||||
*pio_mode = io_mode;
|
*pio_mode = io_mode;
|
||||||
*pbt_mode = bt_mode;
|
*pbt_mode = bt_mode;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *fopen(const char *restrict name, const char *restrict mode) {
|
// Operations on files
|
||||||
DWORD access;
|
|
||||||
DWORD share;
|
int remove(const char *filename) {
|
||||||
DWORD disp;
|
BOOL ok = DeleteFileA(filename);
|
||||||
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
return !ok;
|
||||||
stream_io_mode_t io_mode;
|
}
|
||||||
stream_bt_mode_t bt_mode;
|
|
||||||
if(!win_parse_mode(mode, &io_mode, &bt_mode, &access, &share, &disp)) {
|
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;
|
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);
|
HANDLE handle = CreateFileA(name, access, share, NULL, disp, flags, NULL);
|
||||||
if(handle == INVALID_HANDLE_VALUE) {
|
if(handle == INVALID_HANDLE_VALUE) {
|
||||||
return NULL;
|
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);
|
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;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,15 +263,16 @@ FILE *freopen(const char *restrict name, const char *restrict mode, FILE *restri
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
fflush(stream);
|
fflush(stream);
|
||||||
DWORD access;
|
WindowsMode win_mode;
|
||||||
DWORD share;
|
|
||||||
DWORD disp;
|
|
||||||
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
||||||
stream_io_mode_t io_mode;
|
FileMode io_mode;
|
||||||
stream_bt_mode_t bt_mode;
|
FileType bt_mode;
|
||||||
if(!win_parse_mode(mode, &io_mode, &bt_mode, &access, &share, &disp)) {
|
if(!_mode_parse(mode, &io_mode, &bt_mode, &win_mode)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
DWORD access = win_mode.access;
|
||||||
|
DWORD share = win_mode.share;
|
||||||
|
DWORD disp = win_mode.disp;
|
||||||
if(name == NULL) {
|
if(name == NULL) {
|
||||||
HANDLE handle = ReOpenFile(stream->handle, access, share, flags);
|
HANDLE handle = ReOpenFile(stream->handle, access, share, flags);
|
||||||
if(handle == INVALID_HANDLE_VALUE) {
|
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) {
|
if(handle == INVALID_HANDLE_VALUE) {
|
||||||
return NULL;
|
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;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *tmpfile(void) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fclose(FILE *stream) {
|
int fclose(FILE *stream) {
|
||||||
if(fflush(stream) == EOF) {
|
if(fflush(stream) == EOF) {
|
||||||
return EOF;
|
return EOF;
|
||||||
|
@ -251,17 +303,63 @@ int fclose(FILE *stream) {
|
||||||
if(!CloseHandle(stream->handle)) {
|
if(!CloseHandle(stream->handle)) {
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
if(stream->temp) {
|
||||||
|
BOOL err = DeleteFileA(stream->temp_name);
|
||||||
|
free(stream->temp_name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _close_io() {
|
// Buffering
|
||||||
while(streams_to_close != NULL) {
|
|
||||||
FILE *stream = streams_to_close;
|
int setvbuf(FILE *restrict stream, char *restrict ptr, int mode, size_t size) {
|
||||||
fflush(stream);
|
if(mode != _IOFBF && mode != _IOLBF && mode != _IONBF) {
|
||||||
delete_stream(stream);
|
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) {
|
int feof(FILE *stream) {
|
||||||
return stream->eof;
|
return stream->eof;
|
||||||
}
|
}
|
||||||
|
@ -276,57 +374,14 @@ void clearerr(FILE *stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void perror(char const *s) {
|
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) {
|
// File reads/writes
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fputc(int c, FILE *stream) {
|
int fputc(int c, FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
_file_lock(stream);
|
||||||
stream_buffer_t *buffer = &stream->buffer;
|
FileBuffer *buffer = &stream->buffer;
|
||||||
int res = c;
|
int res = c;
|
||||||
if(buffer->mode == _IONBF) {
|
if(buffer->mode == _IONBF) {
|
||||||
unsigned char str[1] = {c};
|
unsigned char str[1] = {c};
|
||||||
|
@ -335,7 +390,7 @@ int fputc(int c, FILE *stream) {
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
res = EOF;
|
res = EOF;
|
||||||
stream->err = 1;
|
stream->err = 1;
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -349,19 +404,19 @@ int fputc(int c, FILE *stream) {
|
||||||
}
|
}
|
||||||
if(needs_flush) {
|
if(needs_flush) {
|
||||||
if(fflush(stream) == EOF) {
|
if(fflush(stream) == EOF) {
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cum:
|
end:
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fgetc(FILE *stream) {
|
int fgetc(FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
_file_lock(stream);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
stream_buffer_t *buffer = &stream->buffer;
|
FileBuffer *buffer = &stream->buffer;
|
||||||
int read_from_disk = 1;
|
int read_from_disk = 1;
|
||||||
if(buffer->mode != _IONBF) {
|
if(buffer->mode != _IONBF) {
|
||||||
unsigned char *data = buffer->data;
|
unsigned char *data = buffer->data;
|
||||||
|
@ -377,95 +432,47 @@ int fgetc(FILE *stream) {
|
||||||
if(bytes_read != 1) {
|
if(bytes_read != 1) {
|
||||||
res = EOF;
|
res = EOF;
|
||||||
stream->eof = 1;
|
stream->eof = 1;
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
res = EOF;
|
res = EOF;
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
res = buf[0];
|
res = buf[0];
|
||||||
}
|
}
|
||||||
cum:
|
end:
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ungetc(int c, FILE *stream) {
|
int ungetc(int c, FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
_file_lock(stream);
|
||||||
int res;
|
int res;
|
||||||
stream_buffer_t *buffer = &stream->buffer;
|
FileBuffer *buffer = &stream->buffer;
|
||||||
if(buffer->mode == _IONBF) {
|
if(buffer->mode == _IONBF) {
|
||||||
res = EOF;
|
res = EOF;
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(c == EOF) {
|
if(c == EOF) {
|
||||||
res = EOF;
|
res = EOF;
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
if(buffer->written == buffer->size) {
|
if(buffer->written == buffer->size) {
|
||||||
res = EOF;
|
res = EOF;
|
||||||
goto cum;
|
goto end;
|
||||||
}
|
}
|
||||||
unsigned char *data = buffer->data;
|
unsigned char *data = buffer->data;
|
||||||
data[buffer->written++] = (unsigned char)c;
|
data[buffer->written++] = (unsigned char)c;
|
||||||
res = c;
|
res = c;
|
||||||
}
|
}
|
||||||
cum:
|
end:
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return res;
|
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) {
|
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;
|
unsigned char const *bytes = (unsigned char const*)buffer;
|
||||||
size_t num_written;
|
size_t num_written;
|
||||||
for(num_written = 0; num_written < count; ++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;
|
bytes += size;
|
||||||
}
|
}
|
||||||
end:
|
end:
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return num_written;
|
return num_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t fread(void *restrict buffer, size_t size, size_t count, FILE *restrict stream) {
|
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;
|
unsigned char *bytes = (unsigned char *)buffer;
|
||||||
size_t num_read;
|
size_t num_read;
|
||||||
for(num_read = 0; num_read < count; ++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;
|
bytes += size;
|
||||||
}
|
}
|
||||||
end:
|
end:
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return num_read;
|
return num_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,7 +524,7 @@ char *fgets(char *restrict str, int count, FILE *restrict stream) {
|
||||||
str[0] = 0;
|
str[0] = 0;
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
mtx_lock(&stream->lock);
|
_file_lock(stream);
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < count-1; ++i) {
|
for(i = 0; i < count-1; ++i) {
|
||||||
int c = fgetc(stream);
|
int c = fgetc(stream);
|
||||||
|
@ -535,12 +542,12 @@ char *fgets(char *restrict str, int count, FILE *restrict stream) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str[i] = 0;
|
str[i] = 0;
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fputs(char const *str, FILE *stream) {
|
int fputs(char const *str, FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
_file_lock(stream);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
while(*str) {
|
while(*str) {
|
||||||
int c = fputc(*str++, stream);
|
int c = fputc(*str++, stream);
|
||||||
|
@ -549,7 +556,7 @@ int fputs(char const *str, FILE *stream) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mtx_unlock(&stream->lock);
|
_file_unlock(stream);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,3 +569,55 @@ int puts(char const *str) {
|
||||||
char *gets(char *str) {
|
char *gets(char *str) {
|
||||||
return fgets(str, 0x7fffffff, stdin);
|
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_timer(void);
|
||||||
static void _setup_eh();
|
static void _setup_eh();
|
||||||
static void _setup_heap();
|
static void _setup_heap();
|
||||||
static void _setup_io();
|
static void _io_close();
|
||||||
static void _close_io();
|
|
|
@ -2,29 +2,10 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
int main(void)
|
|
||||||
{
|
int main(void) {
|
||||||
// prepare a file holding 4 values of type char
|
FILE *tmp = tmpfile();
|
||||||
enum {SIZE = 4};
|
fputc('a', tmp);
|
||||||
FILE* fp = fopen("test.bin", "wb");
|
fclose(tmp);
|
||||||
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);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue