Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ dkms.conf
*.dwo

build/
compile_commands.json
.cache/
dist/
stdoc.tes/*.out
21 changes: 15 additions & 6 deletions Makefile
Comment thread
uoctamika marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,22 @@ ARFLAGS := rcs

BUILD ?= debug
ifeq ($(BUILD),debug)
CFLAGS := -std=c11 -fPIC -I$(INC_DIR) -g -O0 -Wall -Wextra -Wpedantic -Wstrict-prototypes
LDFLAGS :=
MODETXT := DEBUG
CFLAGS := -std=c11 -fPIC -I$(INC_DIR) -g -O0 -Wall -Wextra -Wpedantic -Wstrict-prototypes
LDFLAGS :=
MODETXT := DEBUG
else ifeq ($(BUILD),release)
CFLAGS := -std=c11 -fPIC -I$(INC_DIR) -O2 -Wall -Wextra
LDFLAGS := -s
MODETXT := RELEASE
else ifeq ($(BUILD),strict)
# Strict mode: all warnings become errors
CFLAGS := -std=c11 -fPIC -I$(INC_DIR) -O0 -g -Wall -Wextra -Wpedantic -Wstrict-prototypes \
-Wconversion -Wsign-conversion -Wshadow -Wcast-align -Wunused -Wnull-dereference \
-Wdouble-promotion -Wformat=2 -Wtrampolines -Wstack-protector -Werror
LDFLAGS := -s
MODETXT := STRICT
else
CFLAGS := -std=c11 -fPIC -I$(INC_DIR) -O2 -Wall -Wextra -DNDEBUG
LDFLAGS := -s
MODETXT := RELEASE
$(error Unknown BUILD mode: $(BUILD). Valid modes: debug, release, strict)
endif

# Shared library flags
Expand Down
105 changes: 52 additions & 53 deletions io/stdoc_scanf.c
Comment thread
uoctamika marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
// stdoc/io/stdoc_scanf.c

#include <stdoc/io/stdoc_scanf.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>

#ifndef STDOC_SCANF_BUF_SIZE
#define STDOC_SCANF_BUF_SIZE 4096
#endif

/*
* syscall_read - raw read from stdin (file descriptor 0)
* Syscall read for aarch64, x86_64, i386, arm.
* Returns number of bytes read, 0 on EOF, -1 on error.
*/
static long syscall_read(void* buf, unsigned long count)
{
long ret;
#if defined(__x86_64__)
#if defined(__aarch64__)
register long x8 __asm__("x8") = 63;
register long x0 __asm__("x0") = 0;
register long x1 __asm__("x1") = (long)buf;
register long x2 __asm__("x2") = count;
__asm__ volatile("svc #0"
: "=r"(x0)
: "r"(x8), "r"(x0), "r"(x1), "r"(x2)
: "memory");
ret = x0;
#elif defined(__x86_64__)
__asm__ volatile("mov $0, %%rax\n"
"syscall"
: "=a"(ret)
Expand All @@ -24,12 +38,6 @@ static long syscall_read(void* buf, unsigned long count)
: "=a"(ret)
: "b"(0), "c"(buf), "d"(count)
: "memory");
#elif defined(__aarch64__)
__asm__ volatile("mov x8, #63\n"
"svc #0"
: "=r"(ret)
: "r"(0), "r"(buf), "r"(count)
: "x8", "memory");
#elif defined(__arm__)
__asm__ volatile("mov r7, #3\n"
"swi #0"
Expand All @@ -43,18 +51,18 @@ static long syscall_read(void* buf, unsigned long count)
}

/*
* Scan context: holds internal buffer and state.
* Buffered input context.
*/
struct scan_ctx {
char* buf; // pointer to buffer
int pos; // current position in buffer
int len; // number of valid bytes in buffer
int eof; // end-of-file flag (non-zero if EOF reached)
char buf[STDOC_SCANF_BUF_SIZE];
int pos; // current read position in buffer
int len; // number of valid bytes in buffer
int eof; // EOF flag
int pushback; // single-character pushback (-1 if none)
};

/*
* Refill buffer from stdin.
* Returns first character read, or -1 on EOF/error.
* Refill buffer from stdin, return first character or -1 on EOF/error.
*/
static int refill(struct scan_ctx* ctx)
{
Expand All @@ -70,30 +78,30 @@ static int refill(struct scan_ctx* ctx)
}

/*
* Return next character from input, consuming it.
* Returns -1 on EOF.
* Get next character, consuming it. Handles pushback and buffer.
*/
static int next_char(struct scan_ctx* ctx)
{
if (ctx->pushback != -1) {
int c = ctx->pushback;
ctx->pushback = -1;
return c;
}
if (ctx->pos < ctx->len)
return (unsigned char)ctx->buf[ctx->pos++];
return refill(ctx);
}

/*
* Push back one character into the buffer.
* Only guarantees one level of unget.
* Push one character back (only one level guaranteed).
*/
static void unget_char(struct scan_ctx* ctx, int c)
{
if (ctx->pos > 0) {
ctx->pos--;
ctx->buf[ctx->pos] = (char)c;
}
ctx->pushback = c;
}

/*
* Skip whitespace characters (space, tab, newline, etc.).
* Skip whitespace: space, tab, newline, carriage return, form feed, vertical tab.
*/
static void skip_whitespace(struct scan_ctx* ctx)
{
Expand All @@ -107,18 +115,19 @@ static void skip_whitespace(struct scan_ctx* ctx)
}

/*
* Read an unsigned integer in given base (10 or 16).
* Read unsigned integer in given base (10 or 16).
* Returns 1 on success, 0 on failure.
*/
static int read_unsigned(struct scan_ctx* ctx, unsigned int* out, int base)
{
skip_whitespace(ctx);
int c = next_char(ctx);
if (c == -1) return 0;

unsigned long val = 0;
int consumed = 0;

// Handle optional 0x prefix for hex
/* Handle optional 0x prefix for hex */
if (base == 16 && c == '0') {
int n = next_char(ctx);
if (n == 'x' || n == 'X') {
Expand All @@ -140,7 +149,7 @@ static int read_unsigned(struct scan_ctx* ctx, unsigned int* out, int base)
digit = c - 'A' + 10;

if (digit < 0 || digit >= base) break;
if (val > (ULONG_MAX - digit) / (unsigned long)base) break; // overflow protection
if (val > (ULONG_MAX - digit) / (unsigned long)base) break; /* overflow protection */

val = val * base + digit;
consumed = 1;
Expand All @@ -153,8 +162,7 @@ static int read_unsigned(struct scan_ctx* ctx, unsigned int* out, int base)
}

/*
* Read a signed decimal integer.
* Returns 1 on success, 0 on failure.
* Read signed decimal integer.
*/
static int read_signed(struct scan_ctx* ctx, int* out)
{
Expand All @@ -171,12 +179,11 @@ static int read_signed(struct scan_ctx* ctx, int* out)
}

if (c == -1 || (c < '0' || c > '9')) return 0;
unget_char(ctx, c);
unget_char(ctx, c); /* put first digit back for read_unsigned */

unsigned int uval;
if (!read_unsigned(ctx, &uval, 10)) return 0;

// Handle signed overflow
if (sign == -1) {
if (uval > (unsigned int)INT_MAX + 1U)
*out = INT_MIN;
Expand All @@ -193,7 +200,6 @@ static int read_signed(struct scan_ctx* ctx, int* out)

/*
* Read a single character (no whitespace skipping).
* Returns 1 on success, 0 on EOF.
*/
static int read_char(struct scan_ctx* ctx, char* out)
{
Expand All @@ -204,9 +210,8 @@ static int read_char(struct scan_ctx* ctx, char* out)
}

/*
* Read a whitespace-delimited string.
* max_len: maximum number of characters to store (including null terminator).
* Returns 1 on success, 0 on failure.
* Read a whitespace-delimited string into buffer, null-terminated.
* max_len includes space for null terminator.
*/
static int read_string(struct scan_ctx* ctx, char* out, size_t max_len)
{
Expand All @@ -227,8 +232,7 @@ static int read_string(struct scan_ctx* ctx, char* out, size_t max_len)
}

/*
* Read a pointer value in hexadecimal.
* Returns 1 on success, 0 on failure.
* Read a pointer value (hexadecimal, optional 0x prefix).
*/
static int read_pointer(struct scan_ctx* ctx, void** out)
{
Expand All @@ -239,7 +243,6 @@ static int read_pointer(struct scan_ctx* ctx, void** out)
unsigned long addr = 0;
int consumed = 0;

// Optional 0x prefix
if (c == '0') {
int n = next_char(ctx);
if (n == 'x' || n == 'X') {
Expand All @@ -261,7 +264,7 @@ static int read_pointer(struct scan_ctx* ctx, void** out)
digit = c - 'A' + 10;
else break;

if (addr > (ULONG_MAX - digit) / 16) break; // overflow protection
if (addr > (ULONG_MAX - digit) / 16) break;
addr = addr * 16 + digit;
consumed = 1;
c = next_char(ctx);
Expand All @@ -273,17 +276,15 @@ static int read_pointer(struct scan_ctx* ctx, void** out)
}

/*
* stdoc_scanf - minimal scanf implementation using direct syscalls.
* Returns number of successfully matched items, or -1 on EOF before any match.
* stdoc_scanf – minimal buffered scanf implementation.
*/
int stdoc_scanf(const char* format, ...)
{
struct scan_ctx ctx;
char buf[STDOC_SCANF_BUF_SIZE];
ctx.buf = buf;
ctx.pos = 0;
ctx.len = 0;
ctx.eof = 0;
ctx.pushback = -1;

va_list args;
va_start(args, format);
Expand All @@ -294,7 +295,7 @@ int stdoc_scanf(const char* format, ...)
while (*p) {
if (*p == '%') {
p++;
if (*p == '%') { // literal '%'
if (*p == '%') { /* literal '%' */
int c = next_char(&ctx);
if (c != '%') {
va_end(args);
Expand All @@ -304,8 +305,7 @@ int stdoc_scanf(const char* format, ...)
continue;
}

// Skip whitespace for all specifiers except %c
bool skip_ws = (*p != 'c');
bool skip_ws = (*p != 'c'); /* skip whitespace except for %c */
if (skip_ws) skip_whitespace(&ctx);

switch (*p) {
Expand Down Expand Up @@ -343,7 +343,7 @@ int stdoc_scanf(const char* format, ...)
}
case 's': {
char* ptr = va_arg(args, char*);
if (read_string(&ctx, ptr, 4096)) // FIXME: no width limit
if (read_string(&ctx, ptr, 4096))
items++;
else
goto done;
Expand All @@ -357,20 +357,19 @@ int stdoc_scanf(const char* format, ...)
goto done;
break;
}
default: // unknown specifier, abort
default: /* unknown specifier, abort */
goto done;
}
p++;
} else if (*p == ' ' || *p == '\t' || *p == '\n') {
// Whitespace in format: skip any whitespace in input
/* Whitespace in format: skip any whitespace in input */
skip_whitespace(&ctx);
p++;
} else {
// Literal character match
/* Literal character match */
int c = next_char(&ctx);
if (c != (unsigned char)*p) {
if (c != (unsigned char)*p)
goto done;
}
p++;
}
}
Expand Down
11 changes: 11 additions & 0 deletions stdoc.tes/index.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdoc.h>

int main(void)
{
int Azi;
int ret;
stdoc_printf("pick a number: ");
ret = stdoc_scanf("%d", &Azi);
stdoc_printf("ret = %d, Azi = %d\n", ret, Azi);
return 0;
}
Loading