diff --git a/src/ce/include/ti_sprintf.h b/src/ce/include/ti_sprintf.h new file mode 100644 index 000000000..2c34e7964 --- /dev/null +++ b/src/ce/include/ti_sprintf.h @@ -0,0 +1,65 @@ +#ifndef TI_SPRINTF_H +#define TI_SPRINTF_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief C89 `sprintf`. `long` arguments and width specifiers are unsupported. + * @note `%s` will write up to 255 characters. + */ +int ti_sprintf( + char *__restrict buffer, const char *__restrict format, ... +) __attribute__((format (__printf__, 2, 3))); + +/** + * @brief Returns an empty string if the output from sprintf does not fit. + * @warning `__VA_ARGS__` is evaluated twice. + * @note Undefined behaviour if the output is longer than ~258000 characters. + */ +#define ti_snprintf(buffer, count, ...)\ +({\ + char * const __buffer = buffer;\ + const int __count = count;\ + int __ret = -1;\ + int __str_len = ti_sprintf((char*)0xFC1000, __VA_ARGS__);\ + if (__buffer == NULL || __count == 0) {\ + __ret = __str_len;\ + } else if ((size_t)__str_len > __count) {\ + *__buffer = '\0'; /* won't fit or invalid formatting */\ + } else {\ + __ret = ti_sprintf(__buffer, __VA_ARGS__);\ + }\ + __ret;\ +}) + +/** + * @brief Allocates a null terminated string containing the output of sprintf. + * The returned pointer shall be deallocated with `free`. + * @warning `__VA_ARGS__` is evaluated twice. + * @note Undefined behaviour if the output is longer than ~258000 characters. + */ +#define ti_asprintf(p_buffer, ...)\ +({\ + char** const __p_buffer = p_buffer;\ + int __ret = -1;\ + int __str_len = ti_sprintf((char*)0xFC1000, __VA_ARGS__);\ + if (__str_len >= 0) {\ + size_t __buffer_size = (size_t)__str_len + 1;\ + *__p_buffer = (char*)malloc(__buffer_size);\ + if (*__p_buffer != NULL) {\ + __ret = ti_sprintf(*__p_buffer, __VA_ARGS__);\ + }\ + }\ + __ret;\ +}) + +#ifdef __cplusplus +} +#endif + +#endif /* TI_SPRINTF_H */ diff --git a/src/libc/ti_routines.src b/src/ce/ti_sprintf.src similarity index 75% rename from src/libc/ti_routines.src rename to src/ce/ti_sprintf.src index 8854e82ab..1097099fb 100644 --- a/src/libc/ti_routines.src +++ b/src/ce/ti_sprintf.src @@ -1,9 +1,9 @@ - assume adl=1 - - section .text - -; to reduce binary size (or performance in the case of sprintf), ti's routines -; can be linked instead of the toolchain's routines - - public __ti_sprintf -__ti_sprintf := 0000BCh + assume adl=1 + + section .text + +; to reduce binary size (or performance in the case of sprintf), ti's routines +; can be linked instead of the toolchain's routines + + public _ti_sprintf +_ti_sprintf := 0000BCh diff --git a/src/libc/errno_str.c b/src/libc/errno_str.c index 636a659ab..9a708ff21 100644 --- a/src/libc/errno_str.c +++ b/src/libc/errno_str.c @@ -2,10 +2,7 @@ #include #include #include - -int _ti_sprintf( - char *__restrict buffer, const char *__restrict format, ... -) __attribute__ ((format (__printf__, 2, 3))); +#include static char const * const errno_strings[] = { "no error", @@ -32,7 +29,7 @@ static_assert( char* strerror(int errnum) { if ((unsigned int)errnum >= errno_strings_count) { - _ti_sprintf(&(unknown_errno_string[unknown_errno_number_offset]), "%d", errnum); + ti_sprintf(&(unknown_errno_string[unknown_errno_number_offset]), "%d", errnum); return (char*)unknown_errno_string; } return (char*)errno_strings[errnum]; @@ -48,8 +45,7 @@ size_t strerrorlen_s(errno_t errnum) { void perror(const char *str) { if (str != NULL && *str != '\0') { fputs(str, stderr); - fputc(':', stderr); - fputc(' ', stderr); + fputs(": ", stderr); } fputs(strerror(errno), stderr); fputc('\n', stderr); diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h index 5cf5bbef6..879239b6e 100644 --- a/src/libc/include/stdio.h +++ b/src/libc/include/stdio.h @@ -89,20 +89,29 @@ int printf(const char *__restrict format, ...) int vprintf(const char *__restrict format, va_list va) __attribute__((format(__printf__, 1, 0))); -int vsprintf(char *__restrict buffer, const char *__restrict format, - va_list va) - __attribute__((format(__printf__, 1, 0))); +int sprintf(char *__restrict buffer, const char *__restrict format, ...) + __attribute__((format (__printf__, 2, 3))); + +int vsprintf(char *__restrict buffer, const char *__restrict format, va_list va) + __attribute__((format(__printf__, 2, 0))); int snprintf(char* buffer, size_t count, const char *__restrict format, ...) __attribute__((format(__printf__, 3, 4))); -int vsnprintf(char* buffer, size_t count, const char *__restrict format, - va_list va) +int vsnprintf(char* buffer, size_t count, const char *__restrict format, va_list va) __attribute__((format(__printf__, 3, 0))); -int sprintf(char *__restrict buffer, - const char *__restrict format, ...) - __attribute__ ((format (__printf__, 2, 3))); +int fprintf(FILE* __restrict stream, const char* __restrict format, ...) + __attribute__((format (__printf__, 2, 3))); + +int vfprintf(FILE* __restrict stream, const char* __restrict format, va_list va) + __attribute__((format(__printf__, 2, 0))); + +int asprintf(char **__restrict p_buffer, const char *__restrict format, ...) + __attribute__((format (__printf__, 2, 3))) __attribute__((nonnull(1))); + +int vasprintf(char **__restrict p_buffer, const char *__restrict format, va_list va) + __attribute__((format(__printf__, 2, 0))) __attribute__((nonnull(1))); void perror(const char* str); diff --git a/src/libc/include/string.h b/src/libc/include/string.h index 937569047..2311a8a90 100644 --- a/src/libc/include/string.h +++ b/src/libc/include/string.h @@ -5,72 +5,104 @@ __BEGIN_DECLS -extern void *memcpy(void *__restrict dest, const void *__restrict src, - size_t n) __attribute__((nonnull(1, 2))); +extern void *memcpy(void *__restrict dest, const void *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); void *memmove(void *dest, const void *src, size_t n) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); -void *memset(void *s, int c, size_t n) __attribute__((nonnull(1))); +void *memset(void *s, int c, size_t n) + __attribute__((nonnull(1))); int memcmp(const void *s1, const void *s2, size_t n) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); -void *memchr(const void *s, int c, size_t n) __attribute__((nonnull(1))); +void *memchr(const void *s, int c, size_t n) + __attribute__((nonnull(1))); + +void *memrchr(const void *s, int c, size_t n) + __NOEXCEPT __attribute__((nonnull(1))) __attribute((__pure__)); + +void *memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) + __NOEXCEPT __attribute__((nonnull(1, 3))) __attribute((__pure__)); + +void *memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n) + __NOEXCEPT __attribute__((nonnull(1, 2))); + +void *mempcpy(void *__restrict dest, const void *__restrict src, size_t n) + __NOEXCEPT __attribute__((nonnull(1, 2))); char *strcpy(char *__restrict dest, const char *__restrict src) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); char *strncpy(char *__restrict dest, const char *__restrict src, size_t n) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); + +char *stpcpy(char *__restrict dest, const char *__restrict src) + __NOEXCEPT __attribute__((nonnull(1, 2))); + +char *stpncpy(char *__restrict dest, const char *__restrict src, size_t n) + __NOEXCEPT __attribute__((nonnull(1, 2))); + +char *strlcpy(char *__restrict dest, const char *__restrict src, size_t n) + __NOEXCEPT __attribute__((nonnull(1, 2))); char *strcat(char *__restrict dest, const char *__restrict src) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); char *strncat(char *__restrict dest, const char *__restrict src, size_t n) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); -char *strchr(const char *s, int c) __attribute__((nonnull(1))); +char *strlcat(char *__restrict dest, const char *__restrict src, size_t n) + __NOEXCEPT __attribute__((nonnull(1, 2))); -char *strrchr(const char *s, int c) __attribute__((nonnull(1))); +char *strchr(const char *s, int c) + __attribute__((nonnull(1))); -char *strpbrk(const char *s, const char *accept) __attribute__((nonnull(1, 2))); +char *strrchr(const char *s, int c) + __attribute__((nonnull(1))); + +char *strpbrk(const char *s, const char *accept) + __attribute__((nonnull(1, 2))); char *strstr(const char *haystack, const char *needle) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); + +char *strcasestr(const char *haystack, const char *needle) + __NOEXCEPT __attribute__((nonnull(1, 2))) __attribute__((__pure__)); char *strtok(char *__restrict s, const char *__restrict delim) - __attribute__((nonnull(2))); + __attribute__((nonnull(2))); char *strdup(const char *s) - __attribute__ ((__malloc__)) __attribute__((nonnull(1))); + __attribute__((__malloc__)) __attribute__((nonnull(1))); char *strndup(const char *s, size_t n) - __attribute__ ((__malloc__)) __attribute__((nonnull(1))); + __attribute__((__malloc__)) __attribute__((nonnull(1))); size_t strcspn(const char *s, const char *reject) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); size_t strspn(const char *s, const char *accept) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); size_t strlen(const char *s) - __attribute__((nonnull(1))); + __attribute__((nonnull(1))); size_t strnlen(const char *s, size_t maxlen) - __attribute__((nonnull(1))); + __attribute__((nonnull(1))); int strcmp(const char *s1, const char *s2) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); int strncmp(const char *s1, const char *s2, size_t n) - __attribute__((nonnull(1, 2))); + __attribute__((nonnull(1, 2))); int strcasecmp(const char *s1, const char *s2) - __attribute__((nonnull(1, 2))); + __NOEXCEPT __attribute__((nonnull(1, 2))) __attribute__((__pure__)); int strncasecmp(const char *s1, const char *s2, size_t n) - __attribute__((nonnull(1, 2))); + __NOEXCEPT __attribute__((nonnull(1, 2))) __attribute__((__pure__)); char* strerror(int errnum); diff --git a/src/libc/include/time.h b/src/libc/include/time.h index 9dc41579a..e5da9ab0a 100644 --- a/src/libc/include/time.h +++ b/src/libc/include/time.h @@ -45,7 +45,8 @@ char *asctime(const struct tm *tmp); char *ctime(const time_t *timer); -size_t strftime(char* ptr, size_t maxsize, const char* format, const struct tm* timeptr); +size_t strftime(char* ptr, size_t maxsize, const char* format, const struct tm* timeptr) + __attribute__((format(__strftime__, 3, 0))); __END_DECLS diff --git a/src/libc/include/wchar.h b/src/libc/include/wchar.h index 44b0efe0d..140babd77 100644 --- a/src/libc/include/wchar.h +++ b/src/libc/include/wchar.h @@ -1,6 +1,8 @@ #ifndef _WCHAR_H #define _WCHAR_H +#include + #ifndef _WCHAR_T_DEFINED #define _WCHAR_T_DEFINED #ifndef __cplusplus @@ -17,4 +19,68 @@ typedef __WCHAR_TYPE__ wchar_t; #define WEOF -1 +__BEGIN_DECLS + +wchar_t *wmemcpy(wchar_t *__restrict dest, const wchar_t *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t n) + __attribute__((nonnull(1, 2))); + +wchar_t *wmemset(wchar_t *s, wchar_t c, size_t n) + __attribute__((nonnull(1))); + +int wmemcmp(const wchar_t *s1, const wchar_t *s2, size_t n) + __attribute__((nonnull(1, 2))); + +wchar_t *wmemchr(const wchar_t *s, int c, size_t n) + __attribute__((nonnull(1))); + +wchar_t *wcscpy(wchar_t *__restrict dest, const wchar_t *__restrict src) + __attribute__((nonnull(1, 2))); + +wchar_t *wcsncpy(wchar_t *__restrict dest, const wchar_t *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +wchar_t *wcscat(wchar_t *__restrict dest, const wchar_t *__restrict src) + __attribute__((nonnull(1, 2))); + +wchar_t *wcsncat(wchar_t *__restrict dest, const wchar_t *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +wchar_t *wcschr(const wchar_t *s, int c) + __attribute__((nonnull(1))); + +wchar_t *wcsrchr(const wchar_t *s, int c) + __attribute__((nonnull(1))); + +wchar_t *wcspbrk(const wchar_t *s, const wchar_t *accept) + __attribute__((nonnull(1, 2))); + +wchar_t *wcsstr(const wchar_t *haystack, const wchar_t *needle) + __attribute__((nonnull(1, 2))); + +wchar_t *wcstok(wchar_t *__restrict s, const wchar_t *__restrict delim) + __attribute__((nonnull(2))); + +size_t wcscspn(const wchar_t *s, const wchar_t *reject) + __attribute__((nonnull(1, 2))); + +size_t wcsspn(const wchar_t *s, const wchar_t *accept) + __attribute__((nonnull(1, 2))); + +size_t wcslen(const wchar_t *s) + __attribute__((nonnull(1))); + +size_t wcsnlen(const wchar_t *s, size_t maxlen) + __attribute__((nonnull(1))); + +int wcscmp(const wchar_t *s1, const wchar_t *s2) + __attribute__((nonnull(1, 2))); + +int wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t n) + __attribute__((nonnull(1, 2))); + +__END_DECLS + #endif /* _WCHAR_H */ diff --git a/src/libc/memccpy.src b/src/libc/memccpy.src new file mode 100644 index 000000000..fa729cd68 --- /dev/null +++ b/src/libc/memccpy.src @@ -0,0 +1,30 @@ + assume adl=1 + + section .text + + public _memccpy + +_memccpy: + ld iy, 0 + add iy, sp + ld bc, (iy + 12) + sbc hl, hl + adc hl, bc + ret z + ld de, (iy + 6) + push de + sbc hl, hl + sbc hl, de + ex de, hl + ld a, (iy + 9) + cpir + add hl, de + ex (sp), hl + pop bc + ld de, (iy + 3) + ldir + ex de, hl + ret z + or a, a + sbc hl, hl + ret diff --git a/src/libc/mempcpy.src b/src/libc/mempcpy.src new file mode 100644 index 000000000..e7a938248 --- /dev/null +++ b/src/libc/mempcpy.src @@ -0,0 +1,41 @@ + assume adl=1 + + section .text + + public _mempcpy + +if 0 + +; faster when count is zero +_mempcpy: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) ; Load count + sbc hl, hl + sbc hl, bc + ld hl, (iy + 3) ; Load destination + ret z ; zero bytes to copy + ld de, (iy + 6) ; Load source + ex de, hl + ldir + ex de, hl + ret + +else + +; faster in full execution case by 0F + 1 clock cycles +_mempcpy: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) ; Load count + sbc hl, hl + sbc hl, bc + ld de, (iy + 3) ; Load destination + jr z, .zero_byte_copy ; zero bytes to copy + ld hl, (iy + 6) ; Load source + ldir +.zero_byte_copy: + ex de, hl + ret + +end if diff --git a/src/libc/nanoprintf.c b/src/libc/nanoprintf.c index f7b869795..16e71a623 100644 --- a/src/libc/nanoprintf.c +++ b/src/libc/nanoprintf.c @@ -48,6 +48,7 @@ #include #include #include +#include /* malloc */ // Pick reasonable defaults if nothing's been configured. #if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ @@ -640,6 +641,10 @@ static void npf_putc_std(int c, void *ctx) { outchar(c); } +static void npf_fputc_std(int c, void *ctx) { + fputc(c, (FILE*)ctx); +} + typedef struct npf_cnt_putc_ctx { npf_putc pc; void *ctx; @@ -978,10 +983,10 @@ int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { #undef NPF_WRITEBACK int _printf_c(char const *format, ...) { - va_list val; - va_start(val, format); - int const rv = vprintf(format, val); - va_end(val); + va_list va; + va_start(va, format); + int const rv = vprintf(format, va); + va_end(va); return rv; } @@ -1005,10 +1010,10 @@ int _vsnprintf_c(char *buffer, size_t bufsz, char const *format, va_list vlist) } int _snprintf_c(char *buffer, size_t bufsz, const char *format, ...) { - va_list val; - va_start(val, format); - int const rv = vsnprintf(buffer, bufsz, format, val); - va_end(val); + va_list va; + va_start(va, format); + int const rv = vsnprintf(buffer, bufsz, format, va); + va_end(va); return rv; } @@ -1031,6 +1036,49 @@ int _sprintf_c(char *buffer, const char *format, ...) return ret; } +int _vasprintf_c(char **__restrict p_str, const char *__restrict format, va_list vlist) { + *p_str = NULL; + int str_len = vsnprintf(NULL, 0, format, vlist); + if (str_len <= 0) { + return str_len; + } + size_t buf_len = (size_t)str_len + 1; + char* buf = (char*)malloc(buf_len); + if (buf == NULL) { + // malloc failure + return -1; + } + int ret = vsnprintf(buf, buf_len, format, vlist); + if (ret <= 0) { + free(buf); + return ret; + } + *p_str = buf; + return ret; +} + +int _asprintf_c(char **__restrict p_str, const char *__restrict format, ...) { + va_list va; + va_start(va, format); + const int ret = vasprintf(p_str, format, va); + va_end(va); + return ret; +} + +int _vfprintf_c(FILE* __restrict stream, const char* __restrict format, va_list vlist) +{ + return npf_vpprintf(npf_fputc_std, (void*)stream, format, vlist); +} + +int _fprintf_c(FILE* __restrict stream, const char* __restrict format, ...) +{ + va_list va; + va_start(va, format); + const int ret = vfprintf(stream, format, va); + va_end(va); + return ret; +} + #if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS #pragma GCC diagnostic pop #endif diff --git a/src/libc/printf.src b/src/libc/printf.src index 4b6f1a01d..c7eb99ce0 100644 --- a/src/libc/printf.src +++ b/src/libc/printf.src @@ -10,20 +10,32 @@ if HAS_PRINTF public _vsprintf public _snprintf public _vsnprintf + public _asprintf + public _vasprintf + public _fprintf + public _vfprintf _printf := __printf_c _vprintf := __vprintf_c +_sprintf := __sprintf_c _vsprintf := __vsprintf_c _snprintf := __snprintf_c _vsnprintf := __vsnprintf_c -_sprintf := __sprintf_c +_asprintf := __asprintf_c +_vasprintf := __vasprintf_c +_fprintf := __fprintf_c +_vfprintf := __vfprintf_c extern __printf_c extern __vprintf_c + extern __sprintf_c extern __vsprintf_c extern __snprintf_c extern __vsnprintf_c - extern __sprintf_c + extern __asprintf_c + extern __vasprintf_c + extern __fprintf_c + extern __vfprintf_c else diff --git a/src/libc/stpcpy.src b/src/libc/stpcpy.src new file mode 100644 index 000000000..17fae2250 --- /dev/null +++ b/src/libc/stpcpy.src @@ -0,0 +1,24 @@ + assume adl=1 + + section .text + + public _stpcpy + +_stpcpy: + pop bc + pop de + ex (sp), hl + push de + push bc + xor a, a + ld bc, 0 + push hl + cpir + sbc hl, hl + sbc hl, bc + ex (sp), hl + pop bc + ldir + ex de, hl + dec hl + ret diff --git a/src/libcxx/include/cstdio b/src/libcxx/include/cstdio index accb75612..fe616946a 100644 --- a/src/libcxx/include/cstdio +++ b/src/libcxx/include/cstdio @@ -31,10 +31,14 @@ using ::putchar; using ::puts; using ::printf; using ::vprintf; +using ::sprintf; using ::vsprintf; using ::snprintf; using ::vsnprintf; -using ::sprintf; +using ::fprintf; +using ::vfprintf; +using ::asprintf; +using ::vasprintf; using ::perror; } // namespace std diff --git a/src/libcxx/include/cstring b/src/libcxx/include/cstring index 90c5655f8..d0622af3e 100644 --- a/src/libcxx/include/cstring +++ b/src/libcxx/include/cstring @@ -14,14 +14,23 @@ using ::memmove; using ::memset; using ::memcmp; using ::memchr; +using ::memrchr; +using ::memmem; +using ::memccpy; +using ::mempcpy; using ::strcpy; using ::strncpy; +using ::stpcpy; +using ::stpncpy; +using ::strlcpy; using ::strcat; using ::strncat; +using ::strlcat; using ::strchr; using ::strrchr; using ::strpbrk; using ::strstr; +using ::strcasestr; using ::strtok; using ::strdup; using ::strndup; diff --git a/test/standalone/asprintf_fprintf/autotest.json b/test/standalone/asprintf_fprintf/autotest.json new file mode 100644 index 000000000..e02763d68 --- /dev/null +++ b/test/standalone/asprintf_fprintf/autotest.json @@ -0,0 +1,40 @@ +{ + "transfer_files": [ + "bin/DEMO.8xp" + ], + "target": { + "name": "DEMO", + "isASM": true + }, + "sequence": [ + "action|launch", + "delay|1500", + "hashWait|1", + "key|enter", + "delay|300", + "hashWait|2" + ], + "hashes": { + "1": { + "description": "All tests passed", + "timeout": 5000, + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "38E2AD5A" + ] + }, + "2": { + "description": "Exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "FFAF89BA", + "101734A5", + "9DA19F44", + "A32840C8", + "349F4775" + ] + } + } +} diff --git a/test/standalone/asprintf_fprintf/makefile b/test/standalone/asprintf_fprintf/makefile new file mode 100644 index 000000000..90ffb4c9d --- /dev/null +++ b/test/standalone/asprintf_fprintf/makefile @@ -0,0 +1,18 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO +ARCHIVED = NO + +CFLAGS = -Wall -Wextra -Wformat=2 -Wshadow -Oz +CXXFLAGS = -Wall -Wextra -Wformat=2 -Wshadow -Oz + +PREFER_OS_LIBC = NO + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c new file mode 100644 index 000000000..05e0b9144 --- /dev/null +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests the following functions/macros: + * ti_sprintf + * ti_snprintf + * ti_asprintf + * asprintf + * fprintf + * stpcpy + * memccpy + */ + +// prevents Clang from replacing function calls with builtins +#if 1 + +void *T_memcpy(void *__restrict dest, const void *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +void *T_memset(void *s, int c, size_t n) + __attribute__((nonnull(1))); + +int T_memcmp(const void *s1, const void *s2, size_t n) + __attribute__((nonnull(1, 2))); + +void *T_memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n) + __attribute__((nonnull(1, 2))); + +void *T_mempcpy(void *__restrict dest, const void *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +char *T_stpcpy(char *__restrict dest, const char *__restrict src) + __attribute__((nonnull(1, 2))); + +size_t T_strlen(const char *s) + __attribute__((nonnull(1))); + +int T_strcmp(const char *s1, const char *s2) + __attribute__((nonnull(1, 2))); + +#else + +#define T_memcpy memcpy +#define T_memset memset +#define T_memcmp memcmp +#define T_memccpy memccpy +#define T_mempcpy mempcpy +#define T_stpcpy stpcpy +#define T_strlen strlen +#define T_strcmp strcmp + +#endif + +static char const * const test_1 = +"+123 asprintf% 076543 0x9abcd 0XFE1 0\n"; +static char const * const test_2 = +"+123 asprintf% 076543 0x9abcd 0XFE1 0\nfoo"; +static const int pos_1 = 30; +static const int pos_2 = 42; + +static char* buf = NULL; +static FILE* file = NULL; + +int ti_tests(void) { + int pos; + int len = ti_asprintf( + &buf, "%+d %s%% %#o %#x %n %#X %i\n", + 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 + ); + if (buf == NULL || len <= 0) { + printf("buf %p len %d\n", buf, len); + return __LINE__; + } + if (buf[len] != '\0') { + return __LINE__; + } + size_t buf_len = T_strlen(buf); + if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { + printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + return __LINE__; + } + if (pos != pos_1) { + printf("E: %d != %d\n", pos, pos_1); + return __LINE__; + } + int cmp = strcmp(buf, test_1); + if (cmp != 0) { + printf("cmp: %d\n", cmp); + return __LINE__; + } + char append[128]; + int snprintf_test = ti_snprintf(append, 20, "%s", test_1); + if (snprintf_test >= 0) { + printf("sprintf_test: %d\n", snprintf_test); + return __LINE__; + } + int len_2 = ti_snprintf(append, sizeof(append), "%s", test_1); + if (len_2 != (int)T_strlen(test_1)) { + printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + return __LINE__; + } + char str2[128]; + char* end; + end = T_stpcpy(str2, append); + end = T_stpcpy(end, ""); + end = T_stpcpy(end, "foo"); + if (*end != '\0') { + return __LINE__; + } + if (end != &str2[pos_2]) { + printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + return __LINE__; + } + int cmp2 = T_strcmp(str2, test_2); + if (cmp2 != 0) { + printf("cmp: %d\n", cmp2); + return __LINE__; + } + return 0; +} + +int nano_tests(void) { + int pos; + int len = asprintf( + &buf, "%+d %s%% %#o %#x %n %#X %i\n", + 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 + ); + if (buf == NULL || len <= 0) { + printf("buf %p len %d\n", buf, len); + return __LINE__; + } + if (buf[len] != '\0') { + return __LINE__; + } + size_t buf_len = T_strlen(buf); + if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { + printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + return __LINE__; + } + if (pos != pos_1) { + printf("E: %d != %d\n", pos, pos_1); + return __LINE__; + } + int cmp = T_strcmp(buf, test_1); + if (cmp != 0) { + printf("cmp: %d\n", cmp); + return __LINE__; + } + return 0; +} + +static char const * const fprintf_test = + "Terminal ':' (found):\t\"Stars:\"\n" + "Terminal ' ' (found):\t\"Stars: \"\n" + "Terminal ',' (found):\t\"Stars: Altair,\"\n" + "Terminal '.' (found):\t\"Stars: Altair, Sun, Vega.\"\n" + "Terminal '!' (absent):\t\"Stars: Altair, Sun, Vega.@\"\n" + "\n" + "Separate star names from distances (ly):\n" + "Arcturus Vega Capella Rigel Procyon \n" +/* fprintf_test */; + +static char const * const file_name = "FPRINTST"; + +static void get_diff_char(const char* data, const char* test) { + size_t char_pos = 0; + char prev = '\0'; + while (*data != '\0' && *test != '\0') { + if (*data != *test) { + printf("prev %02X: %02X != %02X at %zu\n", prev, *data, *test, char_pos); + return; + } + prev = *data; + data++; + test++; + char_pos++; + } +} + +int memccpy_tests(void) { + // test zero byte case + void* ptr = T_memccpy((void*)0xC0FFEE, (void*)0x123456, 123, 0); + if (ptr != NULL) { + printf("%p != NULL\n", ptr); + return __LINE__; + } + file = fopen(file_name, "wb"); + + // Check if the file was opened successfully + if (file == NULL) { + perror("Error opening file"); + return __LINE__; + } + // https://en.cppreference.com/w/c/string/byte/memccpy + const char src[] = "Stars: Altair, Sun, Vega."; + const char terminal[] = {':', ' ', ',', '.', '!'}; + char dest[sizeof src]; + const char alt = '@'; + + for (size_t i = 0; i != sizeof terminal; ++i) + { + void* to = T_memccpy(dest, src, terminal[i], sizeof dest); + + fprintf(file,"Terminal '%c' (%s):\t\"", terminal[i], to ? "found" : "absent"); + + // if `terminal` character was not found - print the whole `dest` + to = to ? to : dest + sizeof dest; + + for (char* from = dest; from != to; ++from) { + fputc(isprint(*from) ? *from : alt, file); + } + + fputs("\"\n", file); + } + + + fprintf(file, "%c%s", '\n', "Separate star names from distances (ly):\n"); + const char *star_distance[] = { + "Arcturus : 37", "Vega : 25", "Capella : 43", "Rigel : 860", "Procyon : 11" + }; + char names_only[64]; + char *first = names_only; + char *last = names_only + sizeof names_only; + + for (size_t t = 0; t != (sizeof star_distance) / (sizeof star_distance[0]); ++t) + { + if (first) { + first = T_memccpy(first, star_distance[t], ' ', last - first); + } else { + break; + } + } + + if (first) { + *first = '\0'; + fprintf(file, "%s%c", names_only, '\n'); + } else { + printf("Error Buffer is too small.\n"); + } + + fseek(file, 0, SEEK_END); + int file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (file_size <= 0) { + perror("file_size <= 0"); + return __LINE__; + } + + buf = (char*)calloc(file_size + 1, sizeof(char)); + if (buf == NULL) { + perror("calloc failure"); + return __LINE__; + } + + size_t bytes_read = fread(buf, 1, file_size, file); + if (bytes_read != (size_t)file_size) { + perror("Error reading from file"); + return __LINE__; + } + if (T_strlen(buf) != T_strlen(fprintf_test)) { + printf("E: %zu != %zu\n", T_strlen(buf), T_strlen(fprintf_test)); + get_diff_char(buf, fprintf_test); + return __LINE__; + } + int cmp = strcmp(buf, fprintf_test); + if (cmp != 0) { + printf("fprintf_cmp: %d\n", cmp); + get_diff_char(buf, fprintf_test); + return __LINE__; + } + return 0; +} + +int mempcpy_test(void) { + // test zero byte case + void* ptr = T_mempcpy((void*)0xC0FFEE, (void*)0x123456, 0); + if (ptr != (void*)0xC0FFEE) { + printf("%p != %p\n", ptr, (void*)0xC0FFEE); + return __LINE__; + } + char data[192 + 1]; + T_memset(&data[ 0], 0x12, 64); + T_memset(&data[ 64], 0x12, 64); + T_memset(&data[128], 0x56, 64); + char append[64 + 1]; + T_memset(append, 0x34, 64); + char* res = T_mempcpy(&data[64], append, 64); + if (res != &data[128]) { + printf("%p != %p\n", res, &data[128]); + return __LINE__; + } + + char truth[192 + 1]; + T_memset(&truth[ 0], 0x12, 64); + T_memset(&truth[ 64], 0x34, 64); + T_memset(&truth[128], 0x56, 64); + + int cmp = T_memcmp(data, truth, 192); + if (cmp != 0) { + printf("cmp: %d\n", cmp); + return __LINE__; + } + return 0; +} + +int run_tests(void) { + int ret = 0; + /* ti_asprintf */ + ret = ti_tests(); + free(buf); buf = NULL; + if (ret != 0) { return ret; } + + /* nano_asprintf */ + ret = nano_tests(); + free(buf); buf = NULL; + if (ret != 0) { return ret; } + + /* nano_fprintf memccpy */ + ret = memccpy_tests(); + free(buf); buf = NULL; + fclose(file); + if (remove(file_name) != 0) { + perror("Couldn't delete file"); + } + if (ret != 0) { return ret; } + + /* mempcpy */ + ret = mempcpy_test(); + if (ret != 0) { return ret; } + + return 0; +} + +int main(void) +{ + os_ClrHome(); + int ret = run_tests(); + if (ret != 0) { + printf("Failed test L%d\n", ret); + } else { + fprintf(stdout, "All tests %s", "passed"); + } + + while (!os_GetCSC()); + + return 0; +} diff --git a/test/standalone/asprintf_fprintf/src/rename.asm b/test/standalone/asprintf_fprintf/src/rename.asm new file mode 100644 index 000000000..5b66c080a --- /dev/null +++ b/test/standalone/asprintf_fprintf/src/rename.asm @@ -0,0 +1,19 @@ + assume adl = 1 + + section .text + + public _T_memset, _T_memcpy, _T_memcmp, _T_memccpy, _T_mempcpy + public _T_strlen, _T_strcmp, _T_stpcpy + +_T_memset := _memset +_T_memcpy := _memcpy +_T_memcmp := _memcmp +_T_memccpy := _memccpy +_T_mempcpy := _mempcpy + +_T_strlen := _strlen +_T_strcmp := _strcmp +_T_stpcpy := _stpcpy + + extern _memset, _memcpy, _memcmp, _memccpy, _mempcpy + extern _strlen, _strcmp, _stpcpy