mirror of https://github.com/flysand7/ciabatta.git
Better handling of stream buffering, implement ungetc
This commit is contained in:
parent
46664047a1
commit
21f0e54e7f
3
bake.cmd
3
bake.cmd
|
@ -1,2 +1,3 @@
|
||||||
|
|
||||||
clang src\unity.c -o ciabatta.lib -c -I inc -I fdec64 -I unicope\inc -I src\win -nodefaultlibs -g -mfma
|
clang src\unity.c -o ciabatta.obj -c -I inc -I fdec64 -I unicope\inc -I src\win -nodefaultlibs -g -mfma
|
||||||
|
lib /out:ciabatta.lib ciabatta.obj fdec64\fdec64.lib unicope\unicope.lib
|
||||||
|
|
12
inc/stdio.h
12
inc/stdio.h
|
@ -2,9 +2,15 @@
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <uchar.h>
|
||||||
|
|
||||||
typedef struct FILE FILE;
|
typedef struct FILE FILE;
|
||||||
typedef size_t fpos_t;
|
|
||||||
|
typedef struct {
|
||||||
|
int64_t pos;
|
||||||
|
mbstate_t mbstate;
|
||||||
|
} fpos_t;
|
||||||
|
|
||||||
#if !defined(NULL)
|
#if !defined(NULL)
|
||||||
#define NULL ((void *)0)
|
#define NULL ((void *)0)
|
||||||
|
@ -90,15 +96,15 @@ int fscanf (FILE *restrict stream, const char *restrict format, ...);
|
||||||
int scanf (const char *restrict format, ...);
|
int scanf (const char *restrict format, ...);
|
||||||
|
|
||||||
// File reading
|
// File reading
|
||||||
|
#define getc fgetc
|
||||||
int fgetc (FILE *stream);
|
int fgetc (FILE *stream);
|
||||||
int getc (FILE *stream);
|
|
||||||
int getchar(void);
|
int getchar(void);
|
||||||
int ungetc (int c, FILE *stream);
|
int ungetc (int c, FILE *stream);
|
||||||
char *fgets (char *restrict s, int n, FILE *restrict stream);
|
char *fgets (char *restrict s, int n, FILE *restrict stream);
|
||||||
size_t fread (void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream);
|
size_t fread (void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream);
|
||||||
|
|
||||||
|
#define putc fputc
|
||||||
int fputc (int c, FILE *stream);
|
int fputc (int c, FILE *stream);
|
||||||
int putc (int c, FILE *stream);
|
|
||||||
int putchar(int c);
|
int putchar(int c);
|
||||||
int fputs (const char *restrict s, FILE *restrict stream);
|
int fputs (const char *restrict s, FILE *restrict stream);
|
||||||
int puts (const char *s);
|
int puts (const char *s);
|
||||||
|
|
|
@ -1,44 +1,46 @@
|
||||||
|
|
||||||
#define __STDC_WANT_LIB_EXT2__ 1
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <win.h>
|
#include <win.h>
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include <threads.h>
|
#include <threads.h>
|
||||||
#include <uchar.h>
|
#include <uchar.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
enum str_type {
|
enum stream_width_t {
|
||||||
STR_R,
|
STREAM_CHAR_UNSET,
|
||||||
STR_W,
|
STREAM_CHAR_NARROW,
|
||||||
STR_A,
|
STREAM_CHAR_WIDE,
|
||||||
} typedef str_type;
|
} typedef stream_width_t;
|
||||||
|
|
||||||
enum str_flags {
|
enum stream_io_mode_t {
|
||||||
STR_U = 1,
|
STREAM_INPUT,
|
||||||
STR_X = 2,
|
STREAM_OUTPUT,
|
||||||
STR_B = 4,
|
STREAM_UPDATE,
|
||||||
} typedef str_flags;
|
} typedef stream_io_mode_t;
|
||||||
|
|
||||||
enum str_mode {
|
enum stream_bt_mode_t {
|
||||||
STR_BIN,
|
STREAM_BINARY,
|
||||||
STR_TEXT,
|
STREAM_TEXT,
|
||||||
} typedef str_mode;
|
} typedef stream_bt_mode_t;
|
||||||
|
|
||||||
|
struct stream_buffer_t typedef stream_buffer_t;
|
||||||
|
struct stream_buffer_t {
|
||||||
|
int is_internal;
|
||||||
|
int mode;
|
||||||
|
size_t size;
|
||||||
|
void *data;
|
||||||
|
size_t written;
|
||||||
|
};
|
||||||
|
|
||||||
struct FILE {
|
struct FILE {
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
char *name;
|
stream_width_t char_width; // This technically needs not be stored
|
||||||
str_type type;
|
|
||||||
str_flags flags;
|
|
||||||
str_mode mode;
|
|
||||||
int buftype;
|
|
||||||
size_t bufsize;
|
|
||||||
int bufidx;
|
|
||||||
char *buffer;
|
|
||||||
mbstate_t mbstate;
|
mbstate_t mbstate;
|
||||||
|
stream_buffer_t buffer;
|
||||||
|
stream_io_mode_t io_mode;
|
||||||
|
stream_bt_mode_t bt_mode;
|
||||||
|
int eof;
|
||||||
|
int err;
|
||||||
mtx_t lock;
|
mtx_t lock;
|
||||||
FILE *prev;
|
FILE *prev;
|
||||||
FILE *next;
|
FILE *next;
|
||||||
|
@ -48,63 +50,59 @@ FILE *stdout;
|
||||||
FILE *stdin;
|
FILE *stdin;
|
||||||
FILE *stderr;
|
FILE *stderr;
|
||||||
|
|
||||||
// Linked list because I'm based and you can't say I'm wrong
|
// We hold a linked list of all file streams in order to flush all the buffers
|
||||||
static FILE *file_list_last = NULL;
|
// 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 inline FILE *new_file(
|
static void close_list_add(FILE *stream) {
|
||||||
HANDLE handle,
|
if(streams_to_close != NULL) {
|
||||||
char *name,
|
streams_to_close->next = stream;
|
||||||
str_type type,
|
stream->prev = streams_to_close;
|
||||||
str_flags flags,
|
stream->next = NULL;
|
||||||
int mode,
|
streams_to_close = stream;
|
||||||
int buftype,
|
|
||||||
size_t bufsize,
|
|
||||||
char *buffer
|
|
||||||
) {
|
|
||||||
FILE *file = malloc(sizeof(FILE));
|
|
||||||
if(file == NULL) return NULL;
|
|
||||||
mtx_init(&file->lock, mtx_recursive);
|
|
||||||
file->handle = handle;
|
|
||||||
file->name = strdup(name);
|
|
||||||
file->type = type;
|
|
||||||
file->flags = flags;
|
|
||||||
file->mode = mode;
|
|
||||||
// Buffer for cache
|
|
||||||
file->buftype = buftype;
|
|
||||||
file->bufsize = bufsize;
|
|
||||||
file->buffer = buffer;
|
|
||||||
file->bufidx = 0;
|
|
||||||
// Multibyte state
|
|
||||||
file->mbstate = (mbstate_t){0};
|
|
||||||
// Append to list of all streams
|
|
||||||
if(file_list_last != NULL) {
|
|
||||||
file_list_last->next = file;
|
|
||||||
file->prev = file_list_last;
|
|
||||||
file->next = NULL;
|
|
||||||
file_list_last = file;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
file_list_last = file;
|
streams_to_close = stream;
|
||||||
file->prev = NULL;
|
stream->prev = NULL;
|
||||||
file->next = NULL;
|
stream->next = NULL;
|
||||||
}
|
}
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void dispose_file(FILE *file) {
|
static void close_list_remove(FILE *stream) {
|
||||||
mtx_t lock = file->lock;
|
FILE *prev = stream->prev;
|
||||||
mtx_lock(&lock);
|
FILE *next = stream->next;
|
||||||
// Only close the file if it's not a standard handle
|
|
||||||
if(file->name) {
|
|
||||||
CloseHandle(file->handle);
|
|
||||||
free(file->name);
|
|
||||||
}
|
|
||||||
FILE *prev = file->prev;
|
|
||||||
FILE *next = file->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) file_list_last = prev;
|
if(next == NULL) streams_to_close = prev;
|
||||||
free(file);
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
mtx_init(&stream->lock, mtx_recursive);
|
||||||
|
close_list_add(stream);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void delete_stream(FILE *stream) {
|
||||||
|
mtx_t lock = stream->lock;
|
||||||
|
mtx_lock(&lock);
|
||||||
|
CloseHandle(stream->handle);
|
||||||
|
close_list_remove(stream);
|
||||||
|
free(stream);
|
||||||
mtx_unlock(&lock);
|
mtx_unlock(&lock);
|
||||||
mtx_destroy(&lock);
|
mtx_destroy(&lock);
|
||||||
}
|
}
|
||||||
|
@ -114,116 +112,216 @@ void _setup_io() {
|
||||||
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);
|
||||||
|
stderr = create_stream(hstderr, STREAM_UPDATE, STREAM_TEXT);
|
||||||
|
stdin = create_stream(hstdin, STREAM_INPUT, STREAM_BINARY);
|
||||||
|
|
||||||
|
char *in_buf = calloc(BUFSIZ, sizeof(char));
|
||||||
char *out_buf = calloc(BUFSIZ, sizeof(char));
|
char *out_buf = calloc(BUFSIZ, sizeof(char));
|
||||||
char *err_buf = out_buf;
|
stdin->buffer = (stream_buffer_t){1, _IOLBF, BUFSIZ, in_buf};
|
||||||
if(hstdout != hstderr) {
|
stdout->buffer = (stream_buffer_t){1, _IOLBF, BUFSIZ, out_buf};
|
||||||
err_buf = calloc(BUFSIZ, sizeof(char));
|
stderr->buffer = (stream_buffer_t){1, _IONBF, 0, NULL};
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout = new_file(hstdout, NULL, STR_W, 0, 0, _IOLBF, BUFSIZ, out_buf);
|
FILE *fopen(const char *restrict name, const char *restrict mode) {
|
||||||
stderr = new_file(hstderr, NULL, STR_W, 0, 0, _IOLBF, BUFSIZ, err_buf);
|
DWORD access = 0;
|
||||||
stdin = new_file(hstdin, NULL, STR_R, 0, 0, _IONBF, 0, NULL);
|
DWORD share = 0;
|
||||||
|
DWORD disp = 0;
|
||||||
|
DWORD flags = FILE_FLAG_WRITE_THROUGH;
|
||||||
|
stream_io_mode_t io_mode = 0;
|
||||||
|
stream_bt_mode_t bt_mode;
|
||||||
|
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;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
while(*mode) switch(*mode++) {
|
||||||
|
case '+': flag_p = 1; break;
|
||||||
|
case 'b': flag_b = 1; break;
|
||||||
|
case 'x': flag_x = 1; break;
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
bt_mode = flag_b? STREAM_BINARY : STREAM_TEXT;
|
||||||
|
// Not sure about the sharing modes
|
||||||
|
switch(io_mode) {
|
||||||
|
case STREAM_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: {
|
||||||
|
access = GENERIC_WRITE | (flag_p? GENERIC_READ : 0);
|
||||||
|
share = 0;
|
||||||
|
disp = CREATE_ALWAYS;
|
||||||
|
if(flag_x) {
|
||||||
|
disp = CREATE_NEW;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case STREAM_UPDATE: {
|
||||||
|
access = GENERIC_READ | GENERIC_WRITE;
|
||||||
|
share = 0;
|
||||||
|
disp = OPEN_ALWAYS;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
HANDLE handle = CreateFileA(name, access, share, NULL, disp, flags, NULL);
|
||||||
|
FILE *stream = create_stream(handle, io_mode, bt_mode);
|
||||||
|
void *buffer_data = malloc(BUFSIZ);
|
||||||
|
stream->buffer = (stream_buffer_t) {1, _IOFBF, BUFSIZ, buffer_data};
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *freopen(const char *restrict name, const char *restrict mode, FILE *restrict stream) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *tmpfile(void) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fclose(FILE *stream) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _close_io() {
|
void _close_io() {
|
||||||
while(file_list_last != NULL) {
|
while(streams_to_close != NULL) {
|
||||||
fflush(file_list_last);
|
FILE *stream = streams_to_close;
|
||||||
dispose_file(file_list_last);
|
fflush(stream);
|
||||||
|
delete_stream(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int setvbuf(
|
int setvbuf(FILE *restrict stream, char *restrict ptr, int mode, size_t size) {
|
||||||
FILE *restrict stream,
|
if(mode != _IOFBF && mode != _IOLBF && mode != _IONBF) {
|
||||||
char *restrict buf,
|
|
||||||
int mode,
|
|
||||||
size_t size
|
|
||||||
) {
|
|
||||||
mtx_lock(&stream->lock);
|
|
||||||
if(mode == _IONBF) {
|
|
||||||
stream->buftype = mode;
|
|
||||||
stream->buffer = NULL;
|
|
||||||
stream->bufsize = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(mode != _IOFBF && mode != _IOLBF)
|
|
||||||
return 1;
|
return 1;
|
||||||
if(buf == NULL && size != 0) {
|
|
||||||
buf = malloc(size);
|
|
||||||
}
|
}
|
||||||
if(size == 0) {
|
mtx_lock(&stream->lock);
|
||||||
buf = NULL;
|
stream_buffer_t *buffer = &stream->buffer;
|
||||||
mode = _IONBF;
|
buffer->mode = mode;
|
||||||
|
if(ptr == NULL) {
|
||||||
|
buffer->data = realloc(buffer->data, size);
|
||||||
|
buffer->size = size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer->data = ptr;
|
||||||
|
buffer->size = size;
|
||||||
}
|
}
|
||||||
stream->buftype = mode;
|
|
||||||
stream->buffer = buf;
|
|
||||||
stream->bufsize = size;
|
|
||||||
mtx_unlock(&stream->lock);
|
mtx_unlock(&stream->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setbuf(FILE *restrict stream, char *restrict buf) {
|
void setbuf(FILE *restrict stream, char *restrict buf) {
|
||||||
int mode = _IOFBF;
|
|
||||||
if(buf == NULL) {
|
if(buf == NULL) {
|
||||||
mode = _IONBF;
|
setvbuf(stream, NULL, _IONBF, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setvbuf(stream, buf, _IOFBF, BUFSIZ);
|
||||||
}
|
}
|
||||||
setvbuf(stream, buf, mode, BUFSIZ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int fflush(FILE *stream) {
|
int fflush(FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
mtx_lock(&stream->lock);
|
||||||
if(stream->buftype != _IONBF) {
|
stream_buffer_t *buffer = &stream->buffer;
|
||||||
DWORD written;
|
void *data = buffer->data;
|
||||||
BOOL ok = WriteFile(
|
size_t size = buffer->written;
|
||||||
stream->handle,
|
DWORD bytes_written;
|
||||||
stream->buffer,
|
BOOL ok = WriteFile(stream->handle, data, size, &bytes_written, NULL);
|
||||||
stream->bufidx,
|
buffer->written = 0;
|
||||||
&written,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
if(!ok) {
|
|
||||||
errno = EIO;
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
stream->bufidx = 0;
|
|
||||||
}
|
|
||||||
mtx_unlock(&stream->lock);
|
mtx_unlock(&stream->lock);
|
||||||
return 0;
|
return (int)ok;
|
||||||
}
|
|
||||||
|
|
||||||
static int try_fputc(FILE *stream, char c) {
|
|
||||||
if(stream->buftype != _IONBF) {
|
|
||||||
stream->buffer[stream->bufidx++] = c;
|
|
||||||
if(stream->bufidx > stream->bufsize) {
|
|
||||||
return fflush(stream);
|
|
||||||
}
|
|
||||||
if(stream->buftype == _IOLBF && c == '\n') {
|
|
||||||
return fflush(stream);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
DWORD written;
|
|
||||||
BOOL ok = WriteFile(stream->handle, &c, 1, &written, NULL);
|
|
||||||
if(!ok) {
|
|
||||||
errno = EIO;
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int fputc(int c, FILE *stream) {
|
int fputc(int c, FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
mtx_lock(&stream->lock);
|
||||||
int res = try_fputc(stream, c);
|
stream_buffer_t *buffer = &stream->buffer;
|
||||||
|
int res = c;
|
||||||
|
if(buffer->mode == _IONBF) {
|
||||||
|
unsigned char str[1] = {c};
|
||||||
|
DWORD bytes_written;
|
||||||
|
BOOL ok = WriteFile(stream->handle, &str, 1, &bytes_written, NULL);
|
||||||
|
if(!ok) {
|
||||||
|
res = 0;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned char *data = buffer->data;
|
||||||
|
data[buffer->written++] = (unsigned char)c;
|
||||||
|
int needs_flush;
|
||||||
|
needs_flush = (buffer->written == buffer->size);
|
||||||
|
if(buffer->mode == _IOLBF) {
|
||||||
|
needs_flush |= (c == '\n');
|
||||||
|
}
|
||||||
|
if(needs_flush) {
|
||||||
|
if(fflush(stream) != 0) {
|
||||||
|
res = 0;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cum:
|
||||||
mtx_unlock(&stream->lock);
|
mtx_unlock(&stream->lock);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fputs(const char *restrict s, FILE *stream) {
|
int fgetc(FILE *stream) {
|
||||||
mtx_lock(&stream->lock);
|
mtx_lock(&stream->lock);
|
||||||
while(*s) {
|
int res = 0;
|
||||||
int res = try_fputc(stream, *s);
|
stream_buffer_t *buffer = &stream->buffer;
|
||||||
if(res == EOF) return EOF;
|
int read_from_disk = 1;
|
||||||
++s;
|
if(buffer->mode != _IONBF) {
|
||||||
|
unsigned char *data = buffer->data;
|
||||||
|
if(buffer->written != 0) {
|
||||||
|
read_from_disk = 0;
|
||||||
|
res = data[--buffer->written];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if(read_from_disk) {
|
||||||
|
unsigned char buf[1];
|
||||||
|
DWORD bytes_read;
|
||||||
|
BOOL ok = ReadFile(stream->handle, buf, 1, &bytes_read, NULL);
|
||||||
|
if(bytes_read != 1) {
|
||||||
|
res = EOF;
|
||||||
|
stream->eof = 1;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
if(!ok) {
|
||||||
|
res = EOF;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
res = buf[0];
|
||||||
|
}
|
||||||
|
cum:
|
||||||
mtx_unlock(&stream->lock);
|
mtx_unlock(&stream->lock);
|
||||||
return 1;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ungetc(int c, FILE *stream) {
|
||||||
|
mtx_lock(&stream->lock);
|
||||||
|
int res;
|
||||||
|
stream_buffer_t *buffer = &stream->buffer;
|
||||||
|
if(buffer->mode == _IONBF) {
|
||||||
|
res = EOF;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(c == EOF) {
|
||||||
|
res = EOF;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
if(buffer->written == buffer->size) {
|
||||||
|
res = EOF;
|
||||||
|
goto cum;
|
||||||
|
}
|
||||||
|
unsigned char *data = buffer->data;
|
||||||
|
data[buffer->written++] = (unsigned char)c;
|
||||||
|
res = c;
|
||||||
|
}
|
||||||
|
cum:
|
||||||
|
mtx_unlock(&stream->lock);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
clang test\test_%test%.c -Iinc -g -luser32 -lkernel32 -lshell32 -lDbghelp -lciabatta.lib
|
|
@ -1,8 +1,38 @@
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int main() {
|
void demo_scanf(const char* fmt, FILE* s)
|
||||||
printf("Hello, %i World!\n", __LINE__);
|
{
|
||||||
|
while (*fmt != '\0') {
|
||||||
|
if (*fmt == '%') {
|
||||||
|
int c;
|
||||||
|
switch (*++fmt) {
|
||||||
|
case 'u':
|
||||||
|
while (isspace(c=getc(s))) {}
|
||||||
|
unsigned int num = 0;
|
||||||
|
while (isdigit(c)) {
|
||||||
|
num = num*10 + c-'0';
|
||||||
|
c = getc(s);
|
||||||
|
}
|
||||||
|
printf("%%u scanned %u\n", num);
|
||||||
|
ungetc(c, s);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
c = getc(s);
|
||||||
|
printf("%%c scanned '%c'\n", c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
++fmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("Please enter a string: ");
|
||||||
|
fflush(stdout);
|
||||||
|
demo_scanf("%u%c", stdin);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
2
unicope
2
unicope
|
@ -1 +1 @@
|
||||||
Subproject commit 0b1530b6a55d9bc3a019fba56c30db0c2827d0ab
|
Subproject commit ec05027e88625488fdbc37814510bd3a8994473d
|
Loading…
Reference in New Issue