From 02e77b5c900e2b19783446adeb3b75359e76734b Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Sat, 21 Feb 2026 22:14:16 +0100 Subject: [PATCH] CE interactive shell --- Makefile | 1 + aux/compiledb.sh | 1 + aux/devel.sh | 1 + aux/docs.sh | 1 + aux/format.sh | 2 + ce/Makefile | 4 + ce/ce.c | 179 +++- init.cmd | 1 - init/init.c | 22 +- kernel/device/ps2_kb.c | 4 + libaux/.gitignore | 4 + libaux/Makefile | 6 + libaux/list.h | 170 ++++ libaux/minmax.h | 7 + libaux/printf.c | 1693 +++++++++++++++++++++++++++++++ libaux/printf.h | 240 +++++ libaux/printf_config.h | 8 + libaux/src.mk | 3 + libkb/kb.c | 4 +- libstring/string.c | 45 + libstring/string.h | 12 + libsystem/str_status.h | 23 + libterminal/ansiq_LICENSE.txt | 623 ++++++++++++ libterminal/ansiq_README.md | 27 + libterminal/terminal_common.h | 6 + libterminal/terminal_cursor.h | 31 + libterminal/terminal_graphics.h | 126 +++ libterminal/terminal_screen.h | 15 + make/dist.mk | 2 +- make/libaux.mk | 16 + 30 files changed, 3259 insertions(+), 18 deletions(-) delete mode 100644 init.cmd create mode 100644 libaux/.gitignore create mode 100644 libaux/Makefile create mode 100644 libaux/list.h create mode 100644 libaux/minmax.h create mode 100644 libaux/printf.c create mode 100644 libaux/printf.h create mode 100644 libaux/printf_config.h create mode 100644 libaux/src.mk create mode 100644 libsystem/str_status.h create mode 100644 libterminal/ansiq_LICENSE.txt create mode 100644 libterminal/ansiq_README.md create mode 100644 libterminal/terminal_common.h create mode 100644 libterminal/terminal_cursor.h create mode 100644 libterminal/terminal_graphics.h create mode 100644 libterminal/terminal_screen.h create mode 100644 make/libaux.mk diff --git a/Makefile b/Makefile index 4462424..33166da 100644 --- a/Makefile +++ b/Makefile @@ -9,3 +9,4 @@ include make/libterminal.mk include make/libprocess.mk include make/libstring.mk include make/libkb.mk +include make/libaux.mk diff --git a/aux/compiledb.sh b/aux/compiledb.sh index 176571e..f33b07e 100755 --- a/aux/compiledb.sh +++ b/aux/compiledb.sh @@ -9,3 +9,4 @@ make -B all_compiledb_libprocess make -B all_compiledb_libterminal make -B all_compiledb_libstring make -B all_compiledb_libkb +make -B all_compiledb_libaux diff --git a/aux/devel.sh b/aux/devel.sh index c78b6d9..57c271e 100755 --- a/aux/devel.sh +++ b/aux/devel.sh @@ -14,6 +14,7 @@ make -B all_libprocess make -B all_libterminal make -B all_libstring make -B all_libkb +make -B all_libaux make -B all_apps make -B all_dist ./aux/limine_iso_amd64.sh diff --git a/aux/docs.sh b/aux/docs.sh index b945783..f658de1 100755 --- a/aux/docs.sh +++ b/aux/docs.sh @@ -10,5 +10,6 @@ make -B docs_libprocess make -B docs_libstring make -B docs_libterminal make -B docs_libkb +make -B docs_libaux mkdocs build diff --git a/aux/format.sh b/aux/format.sh index ddc2a46..62f738d 100755 --- a/aux/format.sh +++ b/aux/format.sh @@ -8,4 +8,6 @@ make -B format_libterminal make -B format_libprocess make -B format_liballoc make -B format_libstring +make -B format_libkb +make -B format_libaux make -B format_apps diff --git a/ce/Makefile b/ce/Makefile index 9bb7685..0dbcb5f 100644 --- a/ce/Makefile +++ b/ce/Makefile @@ -2,5 +2,9 @@ include ../make/ufuncs.mk $(eval $(call add_lib,libstring)) $(eval $(call add_lib,libprocess)) +$(eval $(call add_lib,libaux)) +$(eval $(call add_include,libterminal)) + +cflags += -DPRINTF_INCLUDE_CONFIG_H=1 include ../make/user.mk diff --git a/ce/ce.c b/ce/ce.c index 09d880d..f6f2c73 100644 --- a/ce/ce.c +++ b/ce/ce.c @@ -1,15 +1,178 @@ +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include +#include +#include -void app_main (void) { - int e_pid = get_exec_pid (); - int e_pgid = get_procgroup (e_pid); +#define LINE_BUFFER_MAX 1024 +#define TOKEN_MAX 64 +#define PROMPT "ce $ " - for (;;) { - char ch; +struct token { + struct list_node_link tokens_link; + char buffer[TOKEN_MAX]; +}; - mail_receive (&ch, 1); - /* test (ch); */ - mail_send (e_pgid, &ch, 1); +static int e_pid; +static int e_pgid; + +static void putch (char ch) { mail_send (e_pgid, &ch, 1); } +void putchar_ (char ch) { putch (ch); } + +static bool tokenize_line (void* ctx, const char* start, size_t len) { + struct list_node_link** head = ctx; + + struct token* token = malloc (sizeof (*token)); + + if (token == NULL) + return false; + + memset (token, 0, sizeof (*token)); + memcpy (token->buffer, start, min (sizeof (token->buffer) - 1, len)); + + list_append ((*head), &token->tokens_link); + + return true; +} + +static void free_tokens (struct list_node_link* tokens) { + struct list_node_link *token_link, *token_tmp_link; + list_foreach (tokens, token_link, token_tmp_link) { + struct token* token = list_entry (token_link, struct token, tokens_link); + list_remove (tokens, &token->tokens_link); + free (token); + } +} + +static void cmd_echo (struct list_node_link* tokens) { + struct list_node_link *token_link, *token_tmp_link; + list_foreach (tokens, token_link, token_tmp_link) { + struct token* token = list_entry (token_link, struct token, tokens_link); + + printf ("%s ", token->buffer); + } +} + +static void cmd_cat (struct list_node_link* tokens) { + if (tokens == NULL) { + printf ("ERROR: no file paths provided\n"); + return; + } + + struct fs_desc_buffer desc; + + struct list_node_link *token_link, *token_tmp_link; + list_foreach (tokens, token_link, token_tmp_link) { + struct token* token = list_entry (token_link, struct token, tokens_link); + int ret; + + ret = open (token->buffer); + + if (ret < 0) { + printf ("ERROR opening %s: %s\n", token->buffer, str_status[-ret]); + continue; + } + + describe (token->buffer, &desc); + + if (desc.type != FS_FILE) + goto close1; + + char* buffer = malloc (desc.size + 1); + + if (buffer == NULL) + goto close1; + + memset (buffer, 0, desc.size + 1); + + ret = read (token->buffer, 0, (uint8_t*)buffer, desc.size); + + if (ret < 0) { + printf ("ERROR reading%s: %s\n", token->buffer, str_status[-ret]); + goto close1; + } + + printf ("%s\n", buffer); + + close1: + if (buffer != NULL) + free (buffer); + + close (token->buffer); + } +} + +static void cmd_help (struct list_node_link* tokens) { + (void)tokens; + + printf ("Available commands:\n"); + printf ("\techo ...\n"); + printf ("\thelp\n"); + printf ("\tcat \n"); +} + +static void exec_tokens (struct list_node_link* tokens) { + struct token* cmd_token = list_entry (tokens, struct token, tokens_link); + + if (strcmp (cmd_token->buffer, "echo") == 0) { + cmd_echo (tokens->next); + } else if (strcmp (cmd_token->buffer, "help") == 0) { + cmd_help (tokens->next); + } else if (strcmp (cmd_token->buffer, "cat") == 0) { + cmd_cat (tokens->next); + } else { + printf ("ERROR: unknown command '%s'\n", cmd_token->buffer); + } +} + +static void exec_line (const char* line) { + struct list_node_link* tokens = NULL; + + strtokenize (line, ' ', &tokens, &tokenize_line); + + if (tokens != NULL) + exec_tokens (tokens); + + free_tokens (tokens); +} + +void app_main (void) { + e_pid = get_exec_pid (); + e_pgid = get_procgroup (e_pid); + + size_t line_cursor = 0; + char line_buffer[LINE_BUFFER_MAX + 1]; + memset (line_buffer, 0, sizeof (line_buffer)); + + for (;;) { + printf (PROMPT); + + char ch = 0; + for (;;) { + mail_receive (&ch, 1); + + if (ch == '\n') + break; + + if (line_cursor < LINE_BUFFER_MAX) { + line_buffer[line_cursor++] = ch; + printf ("%c", ch); + } + } + + printf ("\n"); + exec_line (line_buffer); + + line_cursor = 0; + memset (line_buffer, 0, sizeof (line_buffer)); + printf ("\n"); } } diff --git a/init.cmd b/init.cmd deleted file mode 100644 index cd08755..0000000 --- a/init.cmd +++ /dev/null @@ -1 +0,0 @@ -Hello world! diff --git a/init/init.c b/init/init.c index b609bee..32a4949 100644 --- a/init/init.c +++ b/init/init.c @@ -6,9 +6,24 @@ #include #include +static int ce_pgid; + +void receiver (void) { + for (;;) { + char rcv; + mail_receive (&rcv, 1); + if (rcv == '\n') + terminal_print ("\r\n", 2); + else + terminal_print (&rcv, 1); + } +} + void app_main (void) { int ce_pid = exec ("ramdisk:/ce"); - int ce_pgid = get_procgroup (ce_pid); + ce_pgid = get_procgroup (ce_pid); + + process_spawn (&receiver, NULL); for (;;) { int ch = kb_read_key (); @@ -17,10 +32,5 @@ void app_main (void) { continue; mail_send (ce_pgid, (uint8_t*)&ch, 1); - - char rcv; - mail_receive (&rcv, 1); - /* test (rcv); */ - terminal_print (&rcv, 1); } } diff --git a/kernel/device/ps2_kb.c b/kernel/device/ps2_kb.c index 5d1d7b4..4040183 100644 --- a/kernel/device/ps2_kb.c +++ b/kernel/device/ps2_kb.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -205,6 +206,9 @@ int ps2kb_read_key (struct device* device, struct proc* proc, struct reschedule_ uint8_t* chbuf = (uint8_t*)a1; + if (chbuf == NULL) + return -ST_BAD_ADDRESS_SPACE; + spin_lock (&ps2kb_ringbuffer_lock); size_t prev_count = ps2kb_ringbuffer.count; diff --git a/libaux/.gitignore b/libaux/.gitignore new file mode 100644 index 0000000..de276e3 --- /dev/null +++ b/libaux/.gitignore @@ -0,0 +1,4 @@ +*.o +*.json +docs/ +.cache/ diff --git a/libaux/Makefile b/libaux/Makefile new file mode 100644 index 0000000..d27451b --- /dev/null +++ b/libaux/Makefile @@ -0,0 +1,6 @@ +include ../make/ufuncs.mk + +libname := libaux +cflags += -DPRINTF_INCLUDE_CONFIG_H=1 + +include ../make/lib.mk diff --git a/libaux/list.h b/libaux/list.h new file mode 100644 index 0000000..3757260 --- /dev/null +++ b/libaux/list.h @@ -0,0 +1,170 @@ +#ifndef _KERNEL_LIBK_LIST_H +#define _KERNEL_LIBK_LIST_H + +struct list_node_link { + struct list_node_link* next; + struct list_node_link* prev; +}; + +#define list_entry(ptr, type, member) ((type*)((char*)(ptr) - offsetof (type, member))) + +#define list_append(head, new) \ + do { \ + if ((new) != NULL) { \ + (new)->next = NULL; \ + if ((head) != NULL) { \ + struct list_node_link* __tmp = (head); \ + while (__tmp->next != NULL) { \ + __tmp = __tmp->next; \ + } \ + __tmp->next = (new); \ + (new)->prev = __tmp; \ + } else { \ + (new)->prev = NULL; \ + (head) = (new); \ + } \ + } \ + } while (0) + +#define list_prepend(head, new) \ + do { \ + if ((new) != NULL) { \ + (new)->prev = NULL; \ + (new)->next = (head); \ + if ((head) != NULL) { \ + (head)->prev = (new); \ + } \ + (head) = (new); \ + } \ + } while (0) + +#define list_remove(head, ele) \ + do { \ + if ((ele) != NULL) { \ + if ((ele)->prev != NULL) { \ + (ele)->prev->next = (ele)->next; \ + } else { \ + (head) = (ele)->next; \ + } \ + if ((ele)->next != NULL) { \ + (ele)->next->prev = (ele)->prev; \ + } \ + (ele)->next = NULL; \ + (ele)->prev = NULL; \ + } \ + } while (0) + +#define list_find(head, out, propname, propvalue) \ + do { \ + (out) = NULL; \ + struct list_node_link* __tmp = (head); \ + while (__tmp) { \ + if (__tmp->propname == (propvalue)) { \ + (out) = __tmp; \ + break; \ + } \ + __tmp = __tmp->next; \ + } \ + } while (0) + +#define list_foreach(head, var, tmp) \ + for (var = (head), tmp = (var ? var->next : NULL); var != NULL; \ + var = tmp, tmp = (var ? var->next : NULL)) + +#define list_foreach_index(head, var, tmp, idx) \ + for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL; \ + var = tmp, tmp = (var ? var->next : NULL), (idx)++) + +#define list_foreach_index_limit(head, var, tmp, idx, max) \ + for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL && (idx) < (max); \ + var = tmp, tmp = (var ? var->next : NULL), (idx)++) + +#define list_back(head, out) \ + do { \ + (out) = NULL; \ + if ((head) != NULL) { \ + struct list_node_link* __tmp = (head); \ + while (__tmp->next != NULL) { \ + __tmp = __tmp->next; \ + } \ + (out) = __tmp; \ + } \ + } while (0) + +#define list_front(head, out) \ + do { \ + (out) = NULL; \ + if ((head) != NULL) { \ + struct list_node_link* __tmp = (head); \ + while (__tmp->prev != NULL) { \ + __tmp = __tmp->prev; \ + } \ + (out) = __tmp; \ + } \ + } while (0) + +#define list_insert_after(head, pos, new) \ + do { \ + if ((pos) != NULL && (new) != NULL) { \ + (new)->prev = (pos); \ + (new)->next = (pos)->next; \ + if ((pos)->next != NULL) { \ + (pos)->next->prev = (new); \ + } \ + (pos)->next = (new); \ + } else if ((pos) == NULL && (head) == NULL) { \ + (new)->prev = NULL; \ + (new)->next = NULL; \ + (head) = (new); \ + } \ + } while (0) + +#define list_insert_before(head, pos, new) \ + do { \ + if ((pos) != NULL && (new) != NULL) { \ + (new)->next = (pos); \ + (new)->prev = (pos)->prev; \ + if ((pos)->prev != NULL) { \ + (pos)->prev->next = (new); \ + } else { \ + (head) = (new); \ + } \ + (pos)->prev = (new); \ + } else if ((pos) == NULL && (head) == NULL) { \ + (new)->prev = NULL; \ + (new)->next = NULL; \ + (head) = (new); \ + } \ + } while (0) + +#define list_index_of(head, ele, out_idx) \ + do { \ + (out_idx) = -1; \ + int __idx = 0; \ + struct list_node_link* __tmp = (head); \ + while (__tmp != NULL) { \ + if (__tmp == (ele)) { \ + (out_idx) = __idx; \ + break; \ + } \ + __tmp = __tmp->next; \ + __idx++; \ + } \ + } while (0) + +#define list_index_of_prop(head, propname, propvalue, out_idx) \ + do { \ + (out_idx) = -1; \ + int __idx = 0; \ + struct list_node_link* __tmp = (head); \ + while (__tmp != NULL) { \ + if (__tmp->propname == (propvalue)) { \ + (out_idx) = __idx; \ + break; \ + } \ + __tmp = __tmp->next; \ + __idx++; \ + } \ + } while (0) + +#endif // _KERNEL_LIBK_LIST_H diff --git a/libaux/minmax.h b/libaux/minmax.h new file mode 100644 index 0000000..aad8857 --- /dev/null +++ b/libaux/minmax.h @@ -0,0 +1,7 @@ +#ifndef _KERNEL_LIBK_MINMAX_H +#define _KERNEL_LIBK_MINMAX_H + +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#endif // _KERNEL_LIBK_MINMAX_H diff --git a/libaux/printf.c b/libaux/printf.c new file mode 100644 index 0000000..1435341 --- /dev/null +++ b/libaux/printf.c @@ -0,0 +1,1693 @@ +/** + * @author (c) Eyal Rozenberg + * 2021-2024, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. The original code for exponential specifiers was + * contributed by Martijn Jasperse . + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with + * limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* printf doesn't seem to like aggressive optimizations */ +#if defined(__clang__) +#pragma clang optimize off +#endif + +/* + * Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H=1 ...) to include the + * printf_config.h header file + */ +#if PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +#include + +#ifdef __cplusplus +#include +#include +#else +#include +#include +#include +#endif /* __cplusplus */ + +#if !(defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)) +/* C90 */ +#if defined(_MSC_VER) +#define inline __inline +#else +#define inline __inline__ +#endif /* defined(_MSC_VER) */ +#endif /* !(defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)) */ + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +#define printf_ printf +#define sprintf_ sprintf +#define vsprintf_ vsprintf +#define snprintf_ snprintf +#define vsnprintf_ vsnprintf +#define vprintf_ vprintf +#endif /* PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD */ + +/* + * 'ntoa' conversion buffer size, this must be big enough to hold one converted + * numeric number including padded zeros (dynamically created on stack) + */ +#ifndef PRINTF_INTEGER_BUFFER_SIZE +#define PRINTF_INTEGER_BUFFER_SIZE 32 +#endif /* PRINTF_INTEGER_BUFFER_SIZE */ + +/* + * size of the fixed (on-stack) buffer for printing individual decimal numbers. + * this must be big enough to hold one converted floating-point value including + * padded zeros. + */ +#ifndef PRINTF_DECIMAL_BUFFER_SIZE +#define PRINTF_DECIMAL_BUFFER_SIZE 32 +#endif + +/* Support for the decimal notation floating point conversion specifiers (%f, %F) */ +#ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS +#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 +#endif + +/* Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) */ +#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 +#endif + +/* Support for the length write-back specifier (%n) */ +#ifndef PRINTF_SUPPORT_WRITEBACK_SPECIFIER +#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 +#endif + +/* Default precision for the floating point conversion specifiers (the C standard sets this at 6) */ +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6 +#endif + +/* Default choice of type to use for internal floating-point computations */ +#ifndef PRINTF_USE_DOUBLE_INTERNALLY +#define PRINTF_USE_DOUBLE_INTERNALLY 1 +#endif + +/* + * According to the C languages standard, printf() and related functions must be able to print any + * integral number in floating-point notation, regardless of length, when using the %f specifier - + * possibly hundreds of characters, potentially overflowing your buffers. In this implementation, + * all values beyond this threshold are switched to exponential notation. + */ +#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL +#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 +#endif + +/* + * Support for the long long integral types (with the ll, z and t length modifiers for specifiers + * %d,%i,%o,%x,%X,%u, and with the %p specifier). + */ +#ifndef PRINTF_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG 1 +#endif + +/* + * The number of terms in a Taylor series expansion of log_10(x) to + * use for approximation - including the power-zero term (i.e. the + * value at the point of expansion). + */ +#ifndef PRINTF_LOG10_TAYLOR_TERMS +#define PRINTF_LOG10_TAYLOR_TERMS 4 +#endif + +#if PRINTF_LOG10_TAYLOR_TERMS <= 1 +#error "At least one non-constant Taylor expansion is necessary for the log10() calculation" +#endif + +/* + * Be extra-safe, and don't assume format specifiers are completed correctly + * before the format string end. + */ +#ifndef PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER +#define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 1 +#endif + +#define PRINTF_PREFER_DECIMAL false +#define PRINTF_PREFER_EXPONENTIAL true + +/*===========================================================================*/ + +/* The following will convert the number-of-digits into an exponential-notation literal */ +#define PRINTF_CONCATENATE(s1, s2) s1##s2 +#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE (s1, s2) +#define PRINTF_FLOAT_NOTATION_THRESHOLD \ + ((floating_point_t)PRINTF_EXPAND_THEN_CONCATENATE (1e, PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) + +/* internal flag definitions */ +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_INT (1U << 8U) +/* Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS */ +#define FLAGS_LONG (1U << 9U) +#define FLAGS_LONG_LONG (1U << 10U) +#define FLAGS_PRECISION (1U << 11U) +#define FLAGS_ADAPT_EXP (1U << 12U) +#define FLAGS_POINTER (1U << 13U) +/* Note: Similar, but not identical, effect as FLAGS_HASH */ +#define FLAGS_SIGNED (1U << 14U) +#define FLAGS_LONG_DOUBLE (1U << 15U) +/* Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS */ + +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + +#define FLAGS_INT8 FLAGS_CHAR + +#if (SHRT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_SHORT +#elif (INT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_INT +#elif (LONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG +#elif (LLONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 16 bits exactly" +#endif + +#if (SHRT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_SHORT +#elif (INT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_INT +#elif (LONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG +#elif (LLONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 32 bits exactly" +#endif + +#if (SHRT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_SHORT +#elif (INT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_INT +#elif (LONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG +#elif (LLONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 64 bits exactly" +#endif + +#endif /* PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS */ + +typedef unsigned int printf_flags_t; + +#define BASE_BINARY 2 +#define BASE_OCTAL 8 +#define BASE_DECIMAL 10 +#define BASE_HEX 16 + +typedef uint8_t numeric_base_t; + +#if PRINTF_SUPPORT_LONG_LONG +typedef unsigned long long printf_unsigned_value_t; +typedef long long printf_signed_value_t; +#else +typedef unsigned long printf_unsigned_value_t; +typedef long printf_signed_value_t; +#endif /* PRINTF_SUPPORT_LONG_LONG */ + +/* + * The printf()-family functions return an `int`; it is therefore + * unnecessary/inappropriate to use size_t - often larger than int + * in practice - for non-negative related values, such as widths, + * precisions, offsets into buffers used for printing and the sizes + * of these buffers. instead, we use: + */ +typedef unsigned int printf_size_t; +#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX +/* + * If we were to nitpick, this would actually be INT_MAX + 1, + * since INT_MAX is the maximum return value, which excludes the + * trailing '\0'. + */ + +#define SIGN(_negative, _x) ((_negative) ? -(_x) : (_x)) + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) +#include +#if FLT_RADIX != 2 +#error "Non-binary-radix floating-point types are unsupported." +#endif /* PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS */ + +/** + * This library supports taking float-point arguments up to and including + * long double's; but - it currently does _not_ support internal + * representation and manipulation of values as long doubles; the options + * are either single-precision `float` or double-precision `double`. + */ +#if PRINTF_USE_DOUBLE_INTERNALLY +typedef double floating_point_t; +#define FP_TYPE_MANT_DIG DBL_MANT_DIG +#else +typedef float floating_point_t; +#define FP_TYPE_MANT_DIG FLT_MANT_DIG +#endif + +#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 + +#if FP_TYPE_MANT_DIG == 24 + +typedef uint32_t printf_fp_uint_t; +#define FP_TYPE_SIZE_IN_BITS 32 +#define FP_TYPE_EXPONENT_MASK 0xFFU +#define FP_TYPE_BASE_EXPONENT 127 +#define FP_TYPE_MAX FLT_MAX +#define FP_TYPE_MAX_10_EXP FLT_MAX_10_EXP +#define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 +#define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-38f +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 10 + +#elif FP_TYPE_MANT_DIG == 53 + +typedef uint64_t printf_fp_uint_t; +#define FP_TYPE_SIZE_IN_BITS 64 +#define FP_TYPE_EXPONENT_MASK 0x7FFU +#define FP_TYPE_BASE_EXPONENT 1023 +#define FP_TYPE_MAX DBL_MAX +#define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP +#define FP_TYPE_MAX_10_EXP DBL_MAX_10_EXP +#define FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 +#define FP_TYPE_MAX_SUBNORMAL_POWER_OF_10 1e-308 +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T - 1 + +#else /* FP_TYPE_MANT_DIG is neither 24 nor 53 */ +#error "Unsupported floating point type configuration" +#endif /* FP_TYPE_MANT_DIG */ +#define FP_TYPE_STORED_MANTISSA_BITS (FP_TYPE_MANT_DIG - 1) + +typedef union { + printf_fp_uint_t U; + floating_point_t F; +} floating_point_with_bit_access; + +/* + * This is unnecessary in C99, since compound initializers can be used, + * but: + * 1. Some compilers are finicky about this; + * 2. Some people may want to convert this to C89; + * 3. If you try to use it as C++, only C++20 supports compound literals + */ +static inline floating_point_with_bit_access get_bit_access (floating_point_t x) { + floating_point_with_bit_access dwba; + dwba.F = x; + return dwba; +} + +static inline int get_sign_bit (floating_point_t x) { + /* The sign is stored in the highest bit */ + return (int)(get_bit_access (x).U >> (FP_TYPE_SIZE_IN_BITS - 1)); +} + +static inline int get_exp2 (floating_point_with_bit_access x) { + /* + * The exponent in an IEEE-754 floating-point number occupies a contiguous + * sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An + * unsigned offset from some negative value (with the extremal offset values reserved for + * special use). + */ + return (int)((x.U >> FP_TYPE_STORED_MANTISSA_BITS) & FP_TYPE_EXPONENT_MASK) - + FP_TYPE_BASE_EXPONENT; +} +#define PRINTF_ABS(_x) SIGN ((_x) < 0, (_x)) + +#endif /* (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) */ + +/* + * Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid + * and well-defined, but if you're not careful you can easily trigger undefined + * behavior with -LONG_MIN or -LLONG_MIN + */ +#define ABS_FOR_PRINTING(_x) \ + ((printf_unsigned_value_t)((_x) > 0 ? (_x) : -((printf_signed_value_t)_x))) + +/* + * wrapper (used as buffer) for output function type + * + * One of the following must hold: + * 1. max_chars is 0 + * 2. buffer is non-null + * 3. function is non-null + * + * ... otherwise bad things will happen. + */ +typedef struct { + void (*function) (char c, void* extra_arg); + void* extra_function_arg; + char* buffer; + printf_size_t pos; + printf_size_t max_chars; +} output_gadget_t; + +/* + * Note: This function currently assumes it is not passed a '\0' c, + * or alternatively, that '\0' can be passed to the function in the output + * gadget. The former assumption holds within the printf library. It also + * assumes that the output gadget has been properly initialized. + */ +static inline void putchar_via_gadget (output_gadget_t* gadget, char c) { + printf_size_t write_pos = gadget->pos++; + /* + * We're _always_ increasing pos, so as to count how may characters + * _would_ have been written if not for the max_chars limitation + */ + if (write_pos >= gadget->max_chars) { + return; + } + if (gadget->function != NULL) { + /* No check for c == '\0' . */ + gadget->function (c, gadget->extra_function_arg); + } else { + /* + * it must be the case that gadget->buffer != NULL , due to the constraint + * on output_gadget_t ; and note we're relying on write_pos being non-negative. + */ + gadget->buffer[write_pos] = c; + } +} + +/* Possibly-write the string-terminating '\0' character */ +static inline void append_termination_with_gadget (output_gadget_t* gadget) { + printf_size_t null_char_pos; + if (gadget->function != NULL || gadget->max_chars == 0) { + return; + } + if (gadget->buffer == NULL) { + return; + } + null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; + gadget->buffer[null_char_pos] = '\0'; +} + +/* + * We can't use putchar_ as is, since our output gadget + * only takes pointers to functions with an extra argument + */ +static inline void putchar_wrapper (char c, void* unused) { + (void)unused; + putchar_ (c); +} + +static inline output_gadget_t discarding_gadget (void) { + output_gadget_t gadget; + gadget.function = NULL; + gadget.extra_function_arg = NULL; + gadget.buffer = NULL; + gadget.pos = 0; + gadget.max_chars = 0; + return gadget; +} + +static inline output_gadget_t buffer_gadget (char* buffer, size_t buffer_size) { + printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) + ? PRINTF_MAX_POSSIBLE_BUFFER_SIZE + : (printf_size_t)buffer_size; + output_gadget_t result = discarding_gadget (); + if (buffer != NULL) { + result.buffer = buffer; + result.max_chars = usable_buffer_size; + } + return result; +} + +static inline output_gadget_t function_gadget (void (*function) (char, void*), void* extra_arg) { + output_gadget_t result = discarding_gadget (); + result.function = function; + result.extra_function_arg = extra_arg; + result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; + return result; +} + +static inline output_gadget_t extern_putchar_gadget (void) { + return function_gadget (putchar_wrapper, NULL); +} + +/* + * internal secure strlen + * @return The length of the string (excluding the terminating 0) limited by 'maxsize' + * @note strlen uses size_t, but wes only use this function with printf_size_t + * variables - hence the signature. + */ +static inline printf_size_t strnlen_s_ (const char* str, printf_size_t maxsize) { + const char* s; + for (s = str; *s && maxsize--; ++s) + ; + return (printf_size_t)(s - str); +} + +/* + * internal test if char is a digit (0-9) + * @return true if char is a digit + */ +static inline bool is_digit_ (char ch) { return (ch >= '0') && (ch <= '9'); } + +/* internal ASCII string to printf_size_t conversion */ +static printf_size_t atou_ (const char** str) { + printf_size_t i = 0U; + while (is_digit_ (**str)) { + i = i * 10U + (printf_size_t)(*((*str)++) - '0'); + } + return i; +} + +/* output the specified string in reverse, taking care of any zero-padding */ +static void out_rev_ (output_gadget_t* output, const char* buf, printf_size_t len, + printf_size_t width, printf_flags_t flags) { + const printf_size_t start_pos = output->pos; + + /* pad spaces up to given width */ + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + printf_size_t i; + for (i = len; i < width; i++) { + putchar_via_gadget (output, ' '); + } + } + + /* reverse string */ + while (len) { + putchar_via_gadget (output, buf[--len]); + } + + /* append pad spaces up to given width */ + if (flags & FLAGS_LEFT) { + while (output->pos - start_pos < width) { + putchar_via_gadget (output, ' '); + } + } +} + +/* + * Invoked by print_integer after the actual number has been printed, performing necessary + * work on the number's prefix (as the number is initially printed in reverse order) + */ +static void print_integer_finalization (output_gadget_t* output, char* buf, printf_size_t len, + bool negative, numeric_base_t base, printf_size_t precision, + printf_size_t width, printf_flags_t flags) { + printf_size_t unpadded_len = len; + + /* pad with leading zeros */ + { + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + + if (base == BASE_OCTAL && (len > unpadded_len)) { + /* Since we've written some zeros, we've satisfied the alternative format leading space + * requirement */ + flags &= ~FLAGS_HASH; + } + } + + /* handle hash */ + if (flags & (FLAGS_HASH | FLAGS_POINTER)) { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { + /* + * Let's take back some padding digits to fit in what will eventually + * be the format-specific prefix + */ + if (unpadded_len < len) { + len--; /* This should suffice for BASE_OCTAL */ + } + if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) { + len--; /* ... and an extra one for 0x or 0b */ + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'x'; + } else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && + (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'X'; + } else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; /* ignore the space if the '+' exists */ + } else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + out_rev_ (output, buf, len, width, flags); +} + +/* An internal itoa-like function */ +static void print_integer (output_gadget_t* output, printf_unsigned_value_t value, bool negative, + numeric_base_t base, printf_size_t precision, printf_size_t width, + printf_flags_t flags) { + char buf[PRINTF_INTEGER_BUFFER_SIZE]; + printf_size_t len = 0U; + + if (!value) { + if (!(flags & FLAGS_PRECISION)) { + buf[len++] = '0'; + flags &= ~FLAGS_HASH; + /* + * We drop this flag this since either the alternative and regular modes of the specifier + * don't differ on 0 values, or (in the case of octal) we've already provided the special + * handling for this mode. + */ + } else if (base == BASE_HEX) { + flags &= ~FLAGS_HASH; + /* + * We drop this flag this since either the alternative and regular modes of the specifier + * don't differ on 0 values + */ + } + } else { + do { + const char digit = (char)(value % base); + buf[len++] = + (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); + } + + print_integer_finalization (output, buf, len, negative, base, precision, width, flags); +} + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +/* + * Stores a fixed-precision representation of a floating-point number relative + * to a fixed precision (which cannot be determined by examining this structure) + */ +struct floating_point_components { + int_fast64_t integral; + int_fast64_t fractional; + /* + * ... truncation of the actual fractional part of the floating_point_t value, scaled + * by the precision value + */ + bool is_negative; +}; + +static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 + 1] = {1e00, + 1e01, + 1e02, + 1e03, + 1e04, + 1e05, + 1e06, + 1e07, + 1e08, + 1e09, + 1e10 +#if PRINTF_MAX_PRECOMPUTED_POWER_OF_10 > 10 + , + 1e11, + 1e12, + 1e13, + 1e14, + 1e15, + 1e16, + 1e17 +#endif +}; + +/* + * Note: This value does not mean that all floating-point values printed with the + * library will be correct up to this precision; it is just an upper-bound for + * avoiding buffer overruns and such + */ +#define PRINTF_MAX_SUPPORTED_PRECISION (NUM_DECIMAL_DIGITS_IN_INT64_T - 1) + +/* + * Break up a non-negative, finite, floating-point number into two integral + * parts of its decimal representation: The number up to the decimal point, + * and the number appearing after that point - whose number of digits + * corresponds to the precision value. Example: The components of 12.621 + * are 12 and 621 for precision 3, or 12 and 62 for precision 2. + */ +static struct floating_point_components get_components (floating_point_t number, + printf_size_t precision) { + struct floating_point_components number_; + floating_point_t abs_number; + floating_point_t scaled_remainder; + floating_point_t remainder; + const floating_point_t one_half = (floating_point_t)0.5; + number_.is_negative = get_sign_bit (number); + abs_number = SIGN (number_.is_negative, number); + number_.integral = (int_fast64_t)abs_number; + scaled_remainder = (abs_number - (floating_point_t)number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t)scaled_remainder; /* for precision == 0U, this will be 0 */ + + remainder = scaled_remainder - (floating_point_t)number_.fractional; + + if ((remainder > one_half) || + /* Banker's rounding, i.e. round half to even: 1.5 -> 2, but 2.5 -> 2 */ + ((remainder == one_half) && (number_.fractional & 1U))) { + ++number_.fractional; + } + if ((floating_point_t)number_.fractional >= powers_of_10[precision]) { + number_.fractional = 0; + ++number_.integral; + } + + if (precision == 0U) { + remainder = abs_number - (floating_point_t)number_.integral; + if ((remainder == one_half) && (number_.integral & 1U)) { + /* + * Banker's rounding, i.e. round half to even: + * 1.5 -> 2, but 2.5 -> 2 + */ + ++number_.integral; + } + } + return number_; +} + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +struct scaling_factor { + floating_point_t raw_factor; + bool multiply; /* if true, need to multiply by raw_factor; otherwise need to divide by it */ +}; + +static floating_point_t apply_scaling (floating_point_t num, struct scaling_factor normalization) { + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; +} + +static floating_point_t unapply_scaling (floating_point_t normalized, + struct scaling_factor normalization) { +#ifdef __GNUC__ +/* accounting for a static analysis bug in GCC 6.x and earlier */ +#pragma GCC diagnostic push +#if !defined(__has_warning) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif __has_warning("-Wmaybe-uninitialized") +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + return normalization.multiply ? normalized / normalization.raw_factor + : normalized * normalization.raw_factor; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} + +static struct scaling_factor update_normalization (struct scaling_factor sf, + floating_point_t extra_multiplicative_factor) { + struct scaling_factor result; + if (sf.multiply) { + result.multiply = true; + result.raw_factor = sf.raw_factor * extra_multiplicative_factor; + } else { + int factor_exp2 = get_exp2 (get_bit_access (sf.raw_factor)); + int extra_factor_exp2 = get_exp2 (get_bit_access (extra_multiplicative_factor)); + + /* Divide the larger-exponent raw raw_factor by the smaller */ + if (PRINTF_ABS (factor_exp2) > PRINTF_ABS (extra_factor_exp2)) { + result.multiply = false; + result.raw_factor = sf.raw_factor / extra_multiplicative_factor; + } else { + result.multiply = true; + result.raw_factor = extra_multiplicative_factor / sf.raw_factor; + } + } + return result; +} + +static struct floating_point_components +get_normalized_components (bool negative, printf_size_t precision, floating_point_t non_normalized, + struct scaling_factor normalization, int floored_exp10) { + struct floating_point_components components; + floating_point_t scaled; + bool close_to_representation_extremum; + floating_point_t remainder; + floating_point_t prec_power_of_10; + struct scaling_factor account_for_precision; + floating_point_t scaled_remainder; + const floating_point_t rounding_threshold = 0.5; + + components.is_negative = negative; + scaled = apply_scaling (non_normalized, normalization); + + close_to_representation_extremum = ((-floored_exp10 + (int)precision) >= FP_TYPE_MAX_10_EXP - 1); + if (close_to_representation_extremum) { + /* + * We can't have a normalization factor which also accounts for the precision, i.e. moves + * some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. + * So, we'll give up early on getting extra precision... + */ + return get_components (SIGN (negative, scaled), precision); + } + components.integral = (int_fast64_t)scaled; + remainder = + non_normalized - unapply_scaling ((floating_point_t)components.integral, normalization); + prec_power_of_10 = powers_of_10[precision]; + account_for_precision = update_normalization (normalization, prec_power_of_10); + scaled_remainder = apply_scaling (remainder, account_for_precision); + + components.fractional = + (int_fast64_t)scaled_remainder; /* when precision == 0, the assigned value should be 0 */ + scaled_remainder -= + (floating_point_t) + components.fractional; /* when precision == 0, this will not change scaled_remainder */ + + components.fractional += (scaled_remainder >= rounding_threshold); + if (scaled_remainder == rounding_threshold) { + /* banker's rounding: Round towards the even number (making the mean error 0) */ + components.fractional &= ~((int_fast64_t)0x1); + } + /* + * handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), + * and must then be corrected into (1, 0). + * Note: for precision = 0, this will "translate" the rounding effect from + * the fractional part to the integral part where it should actually be + * felt (as prec_power_of_10 is 1) + */ + if ((floating_point_t)components.fractional >= prec_power_of_10) { + components.fractional = 0; + ++components.integral; + } + return components; +} +#endif /* PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS */ + +static void print_broken_up_decimal (struct floating_point_components number_, + output_gadget_t* output, printf_size_t precision, + printf_size_t width, printf_flags_t flags, char* buf, + printf_size_t len) { + if (precision != 0U) { + /* do fractional part, as an unsigned number */ + + printf_size_t count = precision; + + /* %g/%G mandates we skip the trailing 0 digits... */ + if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) { + while (true) { + int_fast64_t digit = number_.fractional % 10U; + if (digit != 0) { + break; + } + --count; + number_.fractional /= 10U; + } + /* + * ... and even the decimal point if there are no + * non-zero fractional part digits (see below) + */ + } + + if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH)) { + while (len < PRINTF_DECIMAL_BUFFER_SIZE) { + --count; + buf[len++] = (char)('0' + number_.fractional % 10U); + if (!(number_.fractional /= 10U)) { + break; + } + } + /* add extra 0s */ + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) { + buf[len++] = '0'; + --count; + } + if (len < PRINTF_DECIMAL_BUFFER_SIZE) { + buf[len++] = '.'; + } + } + } else { + if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { + buf[len++] = '.'; + } + } + + /* + * Write the integer part of the number (it comes after the fractional + * since the character order is reversed) + */ + while (len < PRINTF_DECIMAL_BUFFER_SIZE) { + buf[len++] = (char)('0' + (number_.integral % 10)); + if (!(number_.integral /= 10)) { + break; + } + } + + /* pad leading zeros */ + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_DECIMAL_BUFFER_SIZE) { + if (number_.is_negative) { + buf[len++] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; /* ignore the space if the '+' exists */ + } else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + out_rev_ (output, buf, len, width, flags); +} + +/* internal ftoa for fixed decimal floating point */ +static void print_decimal_number (output_gadget_t* output, floating_point_t number, + printf_size_t precision, printf_size_t width, + printf_flags_t flags, char* buf, printf_size_t len) { + struct floating_point_components value_ = get_components (number, precision); + print_broken_up_decimal (value_, output, precision, width, flags, buf, len); +} + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +/* + * A floor function - but one which only works for numbers whose + * floor value is representable by an int. + */ +static int bastardized_floor (floating_point_t x) { + int n; + if (x >= 0) { + return (int)x; + } + n = (int)x; + return (((floating_point_t)n) == x) ? n : n - 1; +} + +/* + * Computes the base-10 logarithm of the input number - which must be an actual + * positive number (not infinity or NaN, nor a sub-normal) + */ +static floating_point_t log10_of_positive (floating_point_t positive_number) { + /* + * The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). + * + * Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of + * our input number, and need only solve log_10(M) for M between 1 and 2 (as + * the base-2 mantissa is always 1-point-something). In that limited range, a + * Taylor series expansion of log10(x) should serve us well enough; and we'll + * take the mid-point, 1.5, as the point of expansion. + */ + + floating_point_with_bit_access dwba = get_bit_access (positive_number); + /* based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) */ + int exp2 = get_exp2 (dwba); + floating_point_t z; + /* drop the exponent, so dwba.F comes into the range [1,2) */ + dwba.U = (dwba.U & (((printf_fp_uint_t)(1) << FP_TYPE_STORED_MANTISSA_BITS) - 1U)) | + ((printf_fp_uint_t)FP_TYPE_BASE_EXPONENT << FP_TYPE_STORED_MANTISSA_BITS); + z = (dwba.F - (floating_point_t)1.5); + return ( + /* Taylor expansion around 1.5: */ + (floating_point_t)0.1760912590556812420 /* Expansion term 0: ln(1.5) / ln(10) */ + + z * (floating_point_t)0.2895296546021678851 /* Expansion term 1: (M - 1.5) * 2/3 / ln(10) + */ +#if PRINTF_LOG10_TAYLOR_TERMS > 2 + - z * z * (floating_point_t)0.0965098848673892950 /* Expansion term 2: (M - 1.5)^2 * 2/9 / + ln(10) */ +#if PRINTF_LOG10_TAYLOR_TERMS > 3 + + z * z * z * (floating_point_t)0.0428932821632841311 /* Expansion term 2: (M - 1.5)^3 * 8/81 + / ln(10) */ +#endif +#endif + /* exact log_2 of the exponent x, with logarithm base change */ + + (floating_point_t)exp2 * + (floating_point_t)0.30102999566398119521 /* = exp2 * log_10(2) = exp2 * ln(2)/ln(10) */ + ); +} + +static floating_point_t pow10_of_int (int floored_exp10) { + floating_point_with_bit_access dwba; + int exp2; + floating_point_t z; + floating_point_t z2; + + /* A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. + */ + if (floored_exp10 == FP_TYPE_MAX_SUBNORMAL_EXPONENT_OF_10) { + return FP_TYPE_MAX_SUBNORMAL_POWER_OF_10; + } + /* Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow */ + exp2 = bastardized_floor (floored_exp10 * (floating_point_t)3.321928094887362 + + (floating_point_t)0.5); + z = floored_exp10 * (floating_point_t)2.302585092994046 - + exp2 * (floating_point_t)0.6931471805599453; + z2 = z * z; + dwba.U = ((printf_fp_uint_t)(exp2) + FP_TYPE_BASE_EXPONENT) << FP_TYPE_STORED_MANTISSA_BITS; + /* + * compute exp(z) using continued fractions, + * see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + */ + dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + return dwba.F; +} + +static void print_exponential_number (output_gadget_t* output, floating_point_t number, + printf_size_t precision, printf_size_t width, + printf_flags_t flags, char* buf, printf_size_t len) { + const bool negative = get_sign_bit (number); + /* This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + */ + floating_point_t abs_number = SIGN (negative, number); + + int floored_exp10; + bool abs_exp10_covered_by_powers_table; + struct scaling_factor normalization; + struct floating_point_components decimal_part_components; + int original_floored_exp10; + bool fall_back_to_decimal_only_mode; + printf_size_t exp10_part_width; + printf_size_t decimal_part_width; + printf_size_t printed_exponential_start_pos; + + /* Determine the decimal exponent */ + if (abs_number == (floating_point_t)0.0) { + /* TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for + * denormals more generally. */ + floored_exp10 = 0; /* ... and no need to set a normalization factor or check the powers table */ + } else { + floating_point_t exp10 = log10_of_positive (abs_number); + floating_point_t p10; + floored_exp10 = bastardized_floor (exp10); + p10 = pow10_of_int (floored_exp10); + /* correct for rounding errors */ + if (abs_number < p10) { + floored_exp10--; + p10 /= 10; + } + abs_exp10_covered_by_powers_table = + PRINTF_ABS (floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; + normalization.raw_factor = + abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS (floored_exp10)] : p10; + } + + if (flags & FLAGS_ADAPT_EXP) { + /* + * Note: For now, still assuming we _don't_ fall-back to "%f" mode; we can't decide + * that until we've established the exact exponent. + */ + + /* + * In "%g" mode, "precision" is the number of _significant digits_; we must + * "translate" that to an actual number of decimal digits. + */ + precision = (precision > 1) ? (precision - 1U) : 0U; + flags |= FLAGS_PRECISION; /* make sure print_broken_up_decimal respects our choice */ + } + + /* + * We now begin accounting for the widths of the two parts of our printed field: + * the decimal part after decimal exponent extraction, and the base-10 exponent part. + * For both of these, the value of 0 has a special meaning, but not the same one: + * a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width + * means "use as many characters as necessary". + */ + +#ifdef __GNUC__ +/* accounting for a static analysis bug in GCC 6.x and earlier */ +#pragma GCC diagnostic push +#if !defined(__has_warning) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#elif __has_warning("-Wmaybe-uninitialized") +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + decimal_part_components = (floored_exp10 == 0) + ? get_components (SIGN (negative, abs_number), precision) + : get_normalized_components (negative, precision, abs_number, normalization, floored_exp10); + + /* + * Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects + * the exponent and may require additional tweaking of the parts + * (and saving the floored_exp10 in case we'll need to undo the roll-over). + */ + original_floored_exp10 = floored_exp10; + if (decimal_part_components.integral >= 10) { + floored_exp10++; + decimal_part_components.integral = 1; + decimal_part_components.fractional = 0; + } + + /* + * Should we want to fall-back to "%f" mode, and only print the decimal part? + * (and remember we have decreased "precision" by 1 + */ + fall_back_to_decimal_only_mode = + (flags & FLAGS_ADAPT_EXP) && (floored_exp10 >= -4) && (floored_exp10 < (int)precision + 1); + + if (fall_back_to_decimal_only_mode) { + precision = ((int)precision > floored_exp10) ? (unsigned)((int)precision - floored_exp10) : 0U; + /* Redo some work :-) */ + floored_exp10 = original_floored_exp10; + decimal_part_components = get_components (SIGN (negative, abs_number), precision); + if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && + decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) { + floored_exp10++; /* Not strictly necessary, since floored_exp10 is no longer really used */ + if (precision > 0U) { + precision--; + } + /* ... and it should already be the case that decimal_part_components.fractional == 0 */ + } + /* TODO: What about rollover strictly within the fractional part? */ + } + + /* + * the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit + * double is "307" (for 2^1023), so we set aside 4-5 characters overall + */ + exp10_part_width = fall_back_to_decimal_only_mode ? 0U + : (PRINTF_ABS (floored_exp10) < 100) ? 4U + : 5U; + + decimal_part_width = ((flags & FLAGS_LEFT) && exp10_part_width) + ? + /* + * We're padding on the right, so the width constraint is the exponent part's + * problem, not the decimal part's, so we'll use as many characters as we need: + */ + 0U + : + /* + * We're padding on the left; so the width constraint is the decimal part's + * problem. Well, can both the decimal part and the exponent part fit within our overall + * width? + */ + ((width > exp10_part_width) + ? + /* + * Yes, so we limit our decimal part's width. + * (Note this is trivially valid even if we've fallen back to "%f" mode) + */ + width - exp10_part_width + : + /* + * No; we just give up on any restriction on the decimal part and use as many + * characters as we need + */ + 0U); + + printed_exponential_start_pos = output->pos; + print_broken_up_decimal (decimal_part_components, output, precision, decimal_part_width, flags, + buf, len); + + if (!fall_back_to_decimal_only_mode) { + putchar_via_gadget (output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); + print_integer (output, ABS_FOR_PRINTING (floored_exp10), floored_exp10 < 0, 10, 0, + exp10_part_width - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + if (flags & FLAGS_LEFT) { + /* We need to right-pad with spaces to meet the width requirement */ + while (output->pos - printed_exponential_start_pos < width) { + putchar_via_gadget (output, ' '); + } + } + } +} +#endif /* PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS */ + +static void print_floating_point (output_gadget_t* output, floating_point_t value, + printf_size_t precision, printf_size_t width, + printf_flags_t flags, bool prefer_exponential) { + char buf[PRINTF_DECIMAL_BUFFER_SIZE]; + printf_size_t len = 0U; + + /* test for special values */ + if (value != value) { + out_rev_ (output, "nan", 3, width, flags); + return; + } + if (value < -FP_TYPE_MAX) { + out_rev_ (output, "fni-", 4, width, flags); + return; + } + if (value > FP_TYPE_MAX) { + out_rev_ (output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, + flags); + return; + } + + if (!prefer_exponential && + ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) { + /* + * The required behavior of standard printf is to print _every_ integral-part digit -- which + * could mean printing hundreds of characters, overflowing any fixed internal buffer and + * necessitating a more complicated implementation. + */ +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + print_exponential_number (output, value, precision, width, flags, buf, len); +#endif + return; + } + + /* set default precision, if not set explicitly */ + if (!(flags & FLAGS_PRECISION)) { + precision = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + /* limit precision so that our integer holding the fractional part does not overflow */ + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) { + buf[len++] = '0'; /* This respects the precision in terms of result length only */ + precision--; + } + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + if (prefer_exponential) + print_exponential_number (output, value, precision, width, flags, buf, len); + else +#endif + print_decimal_number (output, value, precision, width, flags, buf, len); +} + +#endif /* (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) */ + +/* + * Advances the format pointer past the flags, and returns the parsed flags + * due to the characters passed + */ +static printf_flags_t parse_flags (const char** format) { + printf_flags_t flags = 0U; + do { + switch (**format) { + case '0': + flags |= FLAGS_ZEROPAD; + (*format)++; + break; + case '-': + flags |= FLAGS_LEFT; + (*format)++; + break; + case '+': + flags |= FLAGS_PLUS; + (*format)++; + break; + case ' ': + flags |= FLAGS_SPACE; + (*format)++; + break; + case '#': + flags |= FLAGS_HASH; + (*format)++; + break; + default: + return flags; + } + } while (true); +} + +static inline void format_string_loop (output_gadget_t* output, const char* format, va_list args) { +#if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER +#define ADVANCE_IN_FORMAT_STRING(cptr_) \ + do { \ + (cptr_)++; \ + if (!*(cptr_)) \ + return; \ + } while (0) +#else +#define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ +#endif + + while (*format) { + printf_flags_t flags; + printf_size_t width; + printf_size_t precision; + if (*format != '%') { + /* A regular content character */ + putchar_via_gadget (output, *format); + format++; + continue; + } + /* We're parsing a format specifier: %[flags][width][.precision][length] */ + ADVANCE_IN_FORMAT_STRING (format); + + flags = parse_flags (&format); + + /* evaluate width field */ + width = 0U; + if (is_digit_ (*format)) { + /* + * Note: If the width is negative, we've already parsed its + * sign character '-' as a FLAG_LEFT + */ + width = (printf_size_t)atou_ (&format); + } else if (*format == '*') { + const int w = va_arg (args, int); + if (w < 0) { + flags |= FLAGS_LEFT; /* reverse padding */ + width = (printf_size_t)-w; + } else { + width = (printf_size_t)w; + } + ADVANCE_IN_FORMAT_STRING (format); + } + + /* evaluate precision field */ + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + ADVANCE_IN_FORMAT_STRING (format); + if (*format == '-') { + do { + ADVANCE_IN_FORMAT_STRING (format); + } while (is_digit_ (*format)); + flags &= ~FLAGS_PRECISION; + } else if (is_digit_ (*format)) { + precision = atou_ (&format); + } else if (*format == '*') { + const int precision_ = va_arg (args, int); + if (precision_ < 0) { + flags &= ~FLAGS_PRECISION; + } else { + precision = precision_ > 0 ? (printf_size_t)precision_ : 0U; + } + ADVANCE_IN_FORMAT_STRING (format); + } + } + + /* evaluate length field */ + switch (*format) { +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + case 'I': { + ADVANCE_IN_FORMAT_STRING (format); + /* Greedily parse for size in bits: 8, 16, 32 or 64 */ + switch (*format) { + case '8': + flags |= FLAGS_INT8; + ADVANCE_IN_FORMAT_STRING (format); + break; + case '1': + ADVANCE_IN_FORMAT_STRING (format); + if (*format == '6') { + format++; + flags |= FLAGS_INT16; + } + break; + case '3': + ADVANCE_IN_FORMAT_STRING (format); + if (*format == '2') { + ADVANCE_IN_FORMAT_STRING (format); + flags |= FLAGS_INT32; + } + break; + case '6': + ADVANCE_IN_FORMAT_STRING (format); + if (*format == '4') { + ADVANCE_IN_FORMAT_STRING (format); + flags |= FLAGS_INT64; + } + break; + default: + break; + } + break; + } +#endif + case 'l': + flags |= FLAGS_LONG; + ADVANCE_IN_FORMAT_STRING (format); + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING (format); + } + break; + case 'L': + flags |= FLAGS_LONG_DOUBLE; + ADVANCE_IN_FORMAT_STRING (format); + break; + case 'h': + flags |= FLAGS_SHORT; + ADVANCE_IN_FORMAT_STRING (format); + if (*format == 'h') { + flags |= FLAGS_CHAR; + ADVANCE_IN_FORMAT_STRING (format); + } + break; + case 't': + flags |= (sizeof (ptrdiff_t) <= sizeof (int)) ? FLAGS_INT + : (sizeof (ptrdiff_t) == sizeof (long)) ? FLAGS_LONG + : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING (format); + break; + case 'j': + flags |= (sizeof (intmax_t) == sizeof (long) ? FLAGS_LONG : FLAGS_LONG_LONG); + ADVANCE_IN_FORMAT_STRING (format); + break; + case 'z': + flags |= (sizeof (size_t) <= sizeof (int)) ? FLAGS_INT + : (sizeof (size_t) == sizeof (long)) ? FLAGS_LONG + : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING (format); + break; + default: + break; + } + + /* evaluate specifier */ + switch (*format) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': { + numeric_base_t base; + + if (*format == 'd' || *format == 'i') { + flags |= FLAGS_SIGNED; + } + + if (*format == 'x' || *format == 'X') { + base = BASE_HEX; + } else if (*format == 'o') { + base = BASE_OCTAL; + } else if (*format == 'b') { + base = BASE_BINARY; + } else { + base = BASE_DECIMAL; + flags &= ~FLAGS_HASH; /* decimal integers have no alternative presentation */ + } + + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + format++; + /* ignore '0' flag when precision is given */ + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + if (flags & FLAGS_SIGNED) { + /* A signed specifier: d, i or possibly I + bit size if enabled */ + + if (flags & FLAGS_LONG_LONG) { +#if PRINTF_SUPPORT_LONG_LONG + const long long value = va_arg (args, long long); + print_integer (output, ABS_FOR_PRINTING (value), value < 0, base, precision, width, + flags); +#endif + } else if (flags & FLAGS_LONG) { + const long value = va_arg (args, long); + print_integer (output, ABS_FOR_PRINTING (value), value < 0, base, precision, width, + flags); + } else { + /* + * We never try to interpret the argument as something potentially-smaller than int, + * due to integer promotion rules: Even if the user passed a short int, short unsigned + * etc. - these will come in after promotion, as int's (or unsigned for the case of + * short unsigned when it has the same size as int) + */ + const int value = (flags & FLAGS_CHAR) ? (signed char)va_arg (args, int) + : (flags & FLAGS_SHORT) ? (short int)va_arg (args, int) + : va_arg (args, int); + print_integer (output, ABS_FOR_PRINTING (value), value < 0, base, precision, width, + flags); + } + } else { + /* An unsigned specifier: u, x, X, o, b */ + + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + + if (flags & FLAGS_LONG_LONG) { +#if PRINTF_SUPPORT_LONG_LONG + print_integer (output, (printf_unsigned_value_t)va_arg (args, unsigned long long), false, + base, precision, width, flags); +#endif + } else if (flags & FLAGS_LONG) { + print_integer (output, (printf_unsigned_value_t)va_arg (args, unsigned long), false, base, + precision, width, flags); + } else { + const unsigned int value = (flags & FLAGS_CHAR) + ? (unsigned char)va_arg (args, unsigned int) + : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg (args, unsigned int) + : va_arg (args, unsigned int); + print_integer (output, (printf_unsigned_value_t)value, false, base, precision, width, + flags); + } + } + break; + } +#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS + case 'f': + case 'F': { + floating_point_t value = + (floating_point_t)(flags & FLAGS_LONG_DOUBLE ? va_arg (args, long double) + : va_arg (args, double)); + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + print_floating_point (output, value, precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; + } +#endif +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + case 'e': + case 'E': + case 'g': + case 'G': { + floating_point_t value = + (floating_point_t)(flags & FLAGS_LONG_DOUBLE ? va_arg (args, long double) + : va_arg (args, double)); + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + print_floating_point (output, value, precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; + } +#endif /* PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS */ + case 'c': { + printf_size_t l = 1U; + /* pre padding */ + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + putchar_via_gadget (output, ' '); + } + } + /* char output */ + putchar_via_gadget (output, (char)va_arg (args, int)); + /* post padding */ + if (flags & FLAGS_LEFT) { + while (l++ < width) { + putchar_via_gadget (output, ' '); + } + } + format++; + break; + } + + case 's': { + const char* p = va_arg (args, char*); + if (p == NULL) { + out_rev_ (output, ")llun(", 6, width, flags); + } else { + printf_size_t l = strnlen_s_ (p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); + /* pre padding */ + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + putchar_via_gadget (output, ' '); + } + } + /* string output */ + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { + putchar_via_gadget (output, *(p++)); + --precision; + } + /* post padding */ + if (flags & FLAGS_LEFT) { + while (l++ < width) { + putchar_via_gadget (output, ' '); + } + } + } + format++; + break; + } + + case 'p': { + uintptr_t value; + width = sizeof (void*) * 2U + 2; /* 2 hex chars per byte + the "0x" prefix */ + flags |= FLAGS_ZEROPAD | FLAGS_POINTER; + value = (uintptr_t)va_arg (args, void*); + (value == (uintptr_t)NULL) ? out_rev_ (output, ")lin(", 5, width, flags) + : print_integer (output, (printf_unsigned_value_t)value, false, + BASE_HEX, precision, width, flags); + format++; + break; + } + + case '%': + putchar_via_gadget (output, '%'); + format++; + break; + + /* + * Many people prefer to disable support for %n, as it lets the caller + * engineer a write to an arbitrary location, of a value the caller + * effectively controls - which could be a security concern in some cases. + */ +#if PRINTF_SUPPORT_WRITEBACK_SPECIFIER + case 'n': { + if (flags & FLAGS_CHAR) + *(va_arg (args, char*)) = (char)output->pos; + else if (flags & FLAGS_SHORT) + *(va_arg (args, short*)) = (short)output->pos; + else if (flags & FLAGS_LONG) + *(va_arg (args, long*)) = (long)output->pos; +#if PRINTF_SUPPORT_LONG_LONG + else if (flags & FLAGS_LONG_LONG) + *(va_arg (args, long long*)) = (long long int)output->pos; +#endif /* PRINTF_SUPPORT_LONG_LONG */ + else + *(va_arg (args, int*)) = (int)output->pos; + format++; + break; + } +#endif /* PRINTF_SUPPORT_WRITEBACK_SPECIFIER */ + + default: + putchar_via_gadget (output, *format); + format++; + break; + } + } +} + +/* internal vsnprintf - used for implementing _all library functions */ +static int vsnprintf_impl (output_gadget_t* output, const char* format, va_list args) { + /* + * Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is + * possible to call this function with a non-zero pos value for some "remedial printing". + */ + format_string_loop (output, format, args); + + /* termination */ + append_termination_with_gadget (output); + + /* return written chars without terminating \0 */ + return (int)output->pos; +} + +/*===========================================================================*/ + +int vprintf_ (const char* format, va_list arg) { + output_gadget_t gadget = extern_putchar_gadget (); + return vsnprintf_impl (&gadget, format, arg); +} + +int vsnprintf_ (char* s, size_t n, const char* format, va_list arg) { + output_gadget_t gadget = buffer_gadget (s, n); + return vsnprintf_impl (&gadget, format, arg); +} + +int vsprintf_ (char* s, const char* format, va_list arg) { + return vsnprintf_ (s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); +} + +int vfctprintf (void (*out) (char c, void* extra_arg), void* extra_arg, const char* format, + va_list arg) { + output_gadget_t gadget; + if (out == NULL) { + return 0; + } + gadget = function_gadget (out, extra_arg); + return vsnprintf_impl (&gadget, format, arg); +} + +int printf_ (const char* format, ...) { + int ret; + va_list args; + va_start (args, format); + ret = vprintf_ (format, args); + va_end (args); + return ret; +} + +int sprintf_ (char* s, const char* format, ...) { + int ret; + va_list args; + va_start (args, format); + ret = vsprintf_ (s, format, args); + va_end (args); + return ret; +} + +int snprintf_ (char* s, size_t n, const char* format, ...) { + int ret; + va_list args; + va_start (args, format); + ret = vsnprintf_ (s, n, format, args); + va_end (args); + return ret; +} + +int fctprintf (void (*out) (char c, void* extra_arg), void* extra_arg, const char* format, ...) { + int ret; + va_list args; + va_start (args, format); + ret = vfctprintf (out, extra_arg, format, args); + va_end (args); + return ret; +} diff --git a/libaux/printf.h b/libaux/printf.h new file mode 100644 index 0000000..8f70b69 --- /dev/null +++ b/libaux/printf.h @@ -0,0 +1,240 @@ +/** + * @author (c) Eyal Rozenberg + * 2021-2024, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems + * with a very limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PRINTF_H_ +#define PRINTF_H_ + +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +#ifdef __cplusplus +#include +#include +extern "C" { +#else +#include +#include +#endif + +#ifdef __GNUC__ +#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4) +#define ATTR_PRINTF(one_based_format_index, first_arg) \ + __attribute__ ((format (gnu_printf, (one_based_format_index), (first_arg)))) +#else +#define ATTR_PRINTF(one_based_format_index, first_arg) \ + __attribute__ ((format (printf, (one_based_format_index), (first_arg)))) +#endif +#define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF ((one_based_format_index), 0) +#else +#define ATTR_PRINTF(one_based_format_index, first_arg) +#define ATTR_VPRINTF(one_based_format_index) +#endif + +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 0 +#endif + +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 0 +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +#define printf_ printf +#define sprintf_ sprintf +#define vsprintf_ vsprintf +#define snprintf_ snprintf +#define vsnprintf_ vsnprintf +#define vprintf_ vprintf +#endif + +/* + * If you want to include this implementation file directly rather than + * link against it, this will let you control the functions' visibility, + * e.g. make them static so as not to clash with other objects also + * using them. + */ +#ifndef PRINTF_VISIBILITY +#define PRINTF_VISIBILITY +#endif + +/** + * Prints/send a single character to some opaque output entity + * + * @note This function is not implemented by the library, only declared; you + * must provide an implementation if you wish to use the @ref printf / @ref + * vprintf function (and possibly for linking against the library, if your + * toolchain does not support discarding unused functions) + * + * @note The output could be as simple as a wrapper for the `write()` system + * call on a Unix-like * system, or even libc's @ref putchar , for replicating + * actual functionality of libc's @ref printf * function; but on an embedded + * system it may involve interaction with a special output device, like a UART, + * etc. + * + * @note in libc's @ref putchar, the parameter type is an int; this was intended + * to support the representation of either a proper character or EOF in a + * variable - but this is really not meaningful to pass into @ref putchar and is + * discouraged today. See further discussion in: + * @link https://stackoverflow.com/q/17452847/1593077 + * + * @param c the single character to print + */ +PRINTF_VISIBILITY +void putchar_ (char c); + +/** + * An implementation of the C standard's printf/vprintf + * + * @note you must implement a @ref putchar_ function for using this function - + * it invokes @ref putchar_ * rather than directly performing any I/O (which + * insulates it from any dependence on the operating system * and external + * libraries). + * + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each %-specifier in + * @p format + * @return The number of characters written into @p s, not counting the + * terminating null character + */ +/* @{ */ +PRINTF_VISIBILITY +int printf_ (const char* format, ...) ATTR_PRINTF (1, 2); +PRINTF_VISIBILITY +int vprintf_ (const char* format, va_list arg) ATTR_VPRINTF (1); +/* @} */ + +/** + * An implementation of the C standard's sprintf/vsprintf + * + * @note For security considerations (the potential for exceeding the buffer + * bounds), please consider using the size-constrained variant, @ref snprintf / + * @ref vsnprintf, instead. + * + * @param s An array in which to store the formatted string. It must be large + * enough to fit the formatted output! + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters written into @p s, not counting the + * terminating null character + */ +/* @{ */ +PRINTF_VISIBILITY +int sprintf_ (char* s, const char* format, ...) ATTR_PRINTF (2, 3); +PRINTF_VISIBILITY +int vsprintf_ (char* s, const char* format, va_list arg) ATTR_VPRINTF (2); +/* @} */ + +/** + * An implementation of the C standard's snprintf/vsnprintf + * + * @param s An array in which to store the formatted string. It must be large + * enough to fit either the entire formatted output, or at least @p n + * characters. Alternatively, it can be NULL, in which case nothing will + * be printed, and only the number of characters which _could_ have been + * printed is tallied and returned. + * @param n The maximum number of characters to write to the array, including + * a terminating null character + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters that COULD have been written into @p s, not + * counting the terminating null character. A value equal or larger than + * @p n indicates truncation. Only when the returned value is non-negative + * and less than @p n, the null-terminated string has been fully and + * successfully printed. + */ +/* @{ */ +PRINTF_VISIBILITY +int snprintf_ (char* s, size_t count, const char* format, ...) ATTR_PRINTF (3, 4); +PRINTF_VISIBILITY +int vsnprintf_ (char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF (3); +/* @} */ + +/** + * printf/vprintf with user-specified output function + * + * An alternative to @ref printf_, in which the output function is specified + * dynamically (rather than @ref putchar_ being used) + * + * @param out An output function which takes one character and a type-erased + * additional parameters + * @param extra_arg The type-erased argument to pass to the output function @p + * out with each call + * @param format A string specifying the format of the output, with %-marked + * specifiers of how to interpret additional arguments. + * @param arg Additional arguments to the function, one for each specifier in + * @p format + * @return The number of characters for which the output f unction was invoked, + * not counting the terminating null character + * + */ +PRINTF_VISIBILITY +int fctprintf (void (*out) (char c, void* extra_arg), void* extra_arg, const char* format, ...) + ATTR_PRINTF (3, 4); +PRINTF_VISIBILITY +int vfctprintf (void (*out) (char c, void* extra_arg), void* extra_arg, const char* format, + va_list arg) ATTR_VPRINTF (3); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +#undef printf_ +#undef sprintf_ +#undef vsprintf_ +#undef snprintf_ +#undef vsnprintf_ +#undef vprintf_ +#else +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT +#define printf printf_ +#define sprintf sprintf_ +#define vsprintf vsprintf_ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +#define vprintf vprintf_ +#endif +#endif + +#endif /* PRINTF_H_ */ diff --git a/libaux/printf_config.h b/libaux/printf_config.h new file mode 100644 index 0000000..7380110 --- /dev/null +++ b/libaux/printf_config.h @@ -0,0 +1,8 @@ +#ifndef _KERNEL_LIBK_PRINTF_CONFIG_H +#define _KERNEL_LIBK_PRINTF_CONFIG_H + +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 1 +#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 0 +#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 0 + +#endif // _KERNEL_LIBK_PRINTF_CONFIG_H diff --git a/libaux/src.mk b/libaux/src.mk new file mode 100644 index 0000000..466a385 --- /dev/null +++ b/libaux/src.mk @@ -0,0 +1,3 @@ +c += printf.c + +o += printf.o diff --git a/libkb/kb.c b/libkb/kb.c index bab62e5..9d0634a 100644 --- a/libkb/kb.c +++ b/libkb/kb.c @@ -1,8 +1,8 @@ -#include #include -#include #include #include +#include +#include int kb_read_key (void) { char ch = 0; diff --git a/libstring/string.c b/libstring/string.c index 5abfd33..00aba08 100644 --- a/libstring/string.c +++ b/libstring/string.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -45,3 +46,47 @@ int memcmp (const void* s1, const void* s2, size_t n) { } return 0; } + +void strtokenize (const char* str, char delim, void* ctx, strtokenize_cb_func_t cb) { + const char* start = str; + const char* end; + + while (*start) { + while (*start == delim && *start) + start++; + + if (!*start) + break; + + end = start; + while (*end != delim && *end) + end++; + + if (!cb (ctx, start, (size_t)(end - start))) + break; + + start = end; + } +} + +/* https://stackoverflow.com/a/34873406 */ +int strcmp (const char* s1, const char* s2) { + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return *(const unsigned char*)s1 - *(const unsigned char*)s2; +} + +/* https://stackoverflow.com/a/2490637 */ +char* strcat (char* dest, const char* src) { + char* rdest = dest; + + while (*dest) + dest++; + + while ((*dest++ = *src++)) + ; + + return rdest; +} diff --git a/libstring/string.h b/libstring/string.h index 37406b5..664121c 100644 --- a/libstring/string.h +++ b/libstring/string.h @@ -1,9 +1,12 @@ #ifndef _LIBSTRING_STRING_H #define _LIBSTRING_STRING_H +#include #include #include +typedef bool (*strtokenize_cb_func_t) (void* ctx, const char* start, size_t len); + /* Set block of memory */ size_t memset (void* dst, uint8_t b, size_t n); @@ -19,4 +22,13 @@ size_t strlen (const char* str); /* Compare blocks of memory */ int memcmp (const void* s1, const void* s2, size_t n); +/* tokenize a string */ +void strtokenize (const char* str, char delim, void* ctx, strtokenize_cb_func_t cb); + +/* compare strings */ +int strcmp (const char* s1, const char* s2); + +/* concatinate strings */ +char* strcat (char* dest, const char* src); + #endif // _LIBSTRING_STRING_H diff --git a/libsystem/str_status.h b/libsystem/str_status.h new file mode 100644 index 0000000..9f76afc --- /dev/null +++ b/libsystem/str_status.h @@ -0,0 +1,23 @@ +#ifndef _LIBSYSTEM_STR_STATUS_H +#define _LIBSYSTEM_STR_STATUS_H + +#include + +static const char* str_status[] = { + [ST_OK] = "ok", + [ST_SYSCALL_NOT_FOUND] = "syscall not found", + [ST_UNALIGNED] = "alignment error", + [ST_OOM_ERROR] = "out of memory", + [ST_NOT_FOUND] = "not found", + [ST_BAD_ADDRESS_SPACE] = "pointer outside of virtual address space", + [ST_PERMISSION_ERROR] = "permission error", + [ST_BAD_RESOURCE] = "invalid resource", + [ST_BAD_DEVICE_OP] = "bad device operation", + [ST_EXISTS] = "already exists", + [ST_OOB_ERROR] = "out of bounds access", + [ST_BAD_PATH] = "path parsing error", + [ST_EXEC_ERROR] = "exec error", + [ST_MOUNT_ERROR] = "mount error", +}; + +#endif // _LIBSYSTEM_STR_STATUS_H diff --git a/libterminal/ansiq_LICENSE.txt b/libterminal/ansiq_LICENSE.txt new file mode 100644 index 0000000..261daf4 --- /dev/null +++ b/libterminal/ansiq_LICENSE.txt @@ -0,0 +1,623 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for + software and other kinds of works. + + The licenses for most software and other practical works are designed + to take away your freedom to share and change the works. By contrast, + the GNU General Public License is intended to guarantee your freedom to + share and change all versions of a program--to make sure it remains free + software for all its users. We, the Free Software Foundation, use the + GNU General Public License for most of our software; it applies also to + any other work released this way by its authors. You can apply it to + your programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + them if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you + these rights or asking you to surrender the rights. Therefore, you have + certain responsibilities if you distribute copies of the software, or if + you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must pass on to the recipients the same + freedoms that you received. You must make sure that they, too, receive + or can get the source code. And you must show them these terms so they + know their rights. + + Developers that use the GNU GPL protect your rights with two steps: + (1) assert copyright on the software, and (2) offer you this License + giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains + that there is no warranty for this free software. For both users' and + authors' sake, the GPL requires that modified versions be marked as + changed, so that their problems will not be attributed erroneously to + authors of previous versions. + + Some devices are designed to deny users access to install or run + modified versions of the software inside them, although the manufacturer + can do so. This is fundamentally incompatible with the aim of + protecting users' freedom to change the software. The systematic + pattern of such abuse occurs in the area of products for individuals to + use, which is precisely where it is most unacceptable. Therefore, we + have designed this version of the GPL to prohibit the practice for those + products. If such problems arise substantially in other domains, we + stand ready to extend this provision to those domains in future versions + of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. + States should not allow patents to restrict development and use of + software on general-purpose computers, but in those that do, we wish to + avoid the special danger that patents applied to a free program could + make it effectively proprietary. To prevent this, the GPL assures that + patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of + works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work + in a fashion requiring copyright permission, other than the making of an + exact copy. The resulting work is called a "modified version" of the + earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based + on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on a + computer or modifying a private copy. Propagation includes copying, + distribution (with or without modification), making available to the + public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user through + a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the + work under this License, and how to view a copy of this License. If + the interface presents a list of user commands or options, such as a + menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an official + standard defined by a recognized standards body, or, in the case of + interfaces specified for a particular programming language, one that + is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other + than the work as a whole, that (a) is included in the normal form of + packaging a Major Component, but which is not part of that Major + Component, and (b) serves only to enable use of the work with that + Major Component, or to implement a Standard Interface for which an + implementation is available to the public in source code form. A + "Major Component", in this context, means a major essential component + (kernel, window system, and so on) of the specific operating system + (if any) on which the executable work runs, or a compiler used to + produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts to + control those activities. However, it does not include the work's + System Libraries, or general-purpose tools or generally available free + programs which are used unmodified in performing those activities but + which are not part of the work. For example, Corresponding Source + includes interface definition files associated with source files for + the work, and the source code for shared libraries and dynamically + linked subprograms that the work is specifically designed to require, + such as by intimate data communication or control flow between those + subprograms and other parts of the work. + + The Corresponding Source need not include anything that users + can regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running a + covered work is covered by this License only if the output, given its + content, constitutes a covered work. This License acknowledges your + rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise remains + in force. You may convey covered works to others for the sole purpose + of having them make modifications exclusively for you, or provide you + with facilities for running those works, provided that you comply with + the terms of this License in conveying all material for which you do + not control copyright. Those thus making or running the covered works + for you must do so exclusively on your behalf, under your direction + and control, on terms that prohibit them from making any copies of + your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section 10 + makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under article + 11 of the WIPO copyright treaty adopted on 20 December 1996, or + similar laws prohibiting or restricting circumvention of such + measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such circumvention + is effected by exercising rights under this License with respect to + the covered work, and you disclaim any intention to limit operation or + modification of the work as a means of enforcing, against the work's + users, your or third parties' legal rights to forbid circumvention of + technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the code; + keep intact all notices of the absence of any warranty; and give all + recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered work, + and which are not combined with it such as to form a larger program, + in or on a volume of a storage or distribution medium, is called an + "aggregate" if the compilation and its resulting copyright are not + used to limit the access or legal rights of the compilation's users + beyond what the individual works permit. Inclusion of a covered work + in an aggregate does not cause this License to apply to the other + parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this License, + in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded + from the Corresponding Source as a System Library, need not be + included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any + tangible personal property which is normally used for personal, family, + or household purposes, or (2) anything designed or sold for incorporation + into a dwelling. In determining whether a product is a consumer product, + doubtful cases shall be resolved in favor of coverage. For a particular + product received by a particular user, "normally used" refers to a + typical or common use of that class of product, regardless of the status + of the particular user or of the way in which the particular user + actually uses, or expects or is expected to use, the product. A product + is a consumer product regardless of whether the product has substantial + commercial, industrial or non-consumer uses, unless such uses represent + the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to install + and execute modified versions of a covered work in that User Product from + a modified version of its Corresponding Source. The information must + suffice to ensure that the continued functioning of the modified object + code is in no case prevented or interfered with solely because + modification has been made. + + If you convey an object code work under this section in, or with, or + specifically for use in, a User Product, and the conveying occurs as + part of a transaction in which the right of possession and use of the + User Product is transferred to the recipient in perpetuity or for a + fixed term (regardless of how the transaction is characterized), the + Corresponding Source conveyed under this section must be accompanied + by the Installation Information. But this requirement does not apply + if neither you nor any third party retains the ability to install + modified object code on the User Product (for example, the work has + been installed in ROM). + + The requirement to provide Installation Information does not include a + requirement to continue to provide support service, warranty, or updates + for a work that has been modified or installed by the recipient, or for + the User Product in which it has been modified or installed. Access to a + network may be denied when the modification itself materially and + adversely affects the operation of the network or violates the rules and + protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, + in accord with this section must be in a format that is publicly + documented (and with an implementation available to the public in + source code form), and must require no special password or key for + unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this + License by making exceptions from one or more of its conditions. + Additional permissions that are applicable to the entire Program shall + be treated as though they were included in this License, to the extent + that they are valid under applicable law. If additional permissions + apply only to part of the Program, that part may be used separately + under those permissions, but the entire Program remains governed by + this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part of + it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you + add to a covered work, you may (if authorized by the copyright holders of + that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as you + received it, or any part of it, contains a notice stating that it is + governed by this License along with a term that is a further + restriction, you may remove that term. If a license document contains + a further restriction but permits relicensing or conveying under this + License, you may add to a covered work material governed by the terms + of that license document, provided that the further restriction does + not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the + form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights under + this License (including any patent licenses granted under the third + paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the copyright + holder fails to notify you of the violation by some reasonable means + prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from that + copyright holder, and you cure the violation prior to 30 days after + your receipt of the notice. + + Termination of your rights under this section does not terminate the + licenses of parties who have received copies or rights from you under + this License. If your rights have been terminated and not permanently + reinstated, you do not qualify to receive new licenses for the same + material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer transmission + to receive a copy likewise does not require acceptance. However, + nothing other than this License grants you permission to propagate or + modify any covered work. These actions infringe copyright if you do + not accept this License. Therefore, by modifying or propagating a + covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not responsible + for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a covered + work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or could + give under the previous paragraph, plus a right to possession of the + Corresponding Source of the work from the predecessor in interest, if + the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you may + not impose a license fee, royalty, or other charge for exercise of + rights granted under this License, and you may not initiate litigation + (including a cross-claim or counterclaim in a lawsuit) alleging that + any patent claim is infringed by making, using, selling, offering for + sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. The + work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, permitted + by this License, of making, using, or selling its contributor version, + but do not include claims that would be infringed only as a + consequence of further modification of the contributor version. For + purposes of this definition, "control" includes the right to grant + patent sublicenses in a manner consistent with the requirements of + this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free + patent license under the contributor's essential patent claims, to + make, use, sell, offer for sale, import and otherwise run, modify and + propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express + agreement or commitment, however denominated, not to enforce a patent + (such as an express permission to practice a patent or covenant not to + sue for patent infringement). To "grant" such a patent license to a + party means to make such an agreement or commitment not to enforce a + patent against the party. + + If you convey a covered work, knowingly relying on a patent license, + and the Corresponding Source of the work is not available for anyone + to copy, free of charge and under the terms of this License, through a + publicly available network server or other readily accessible means, + then you must either (1) cause the Corresponding Source to be so + available, or (2) arrange to deprive yourself of the benefit of the + patent license for this particular work, or (3) arrange, in a manner + consistent with the requirements of this License, to extend the patent + license to downstream recipients. "Knowingly relying" means you have + actual knowledge that, but for the patent license, your conveying the + covered work in a country, or your recipient's use of the covered work + in a country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, modify + or convey a specific copy of the covered work, then the patent license + you grant is automatically extended to all recipients of the covered + work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that are + specifically granted under this License. You may not convey a covered + work if you are a party to an arrangement with a third party that is + in the business of distributing software, under which you make payment + to the third party based on the extent of your activity of conveying + the work, and under which the third party grants, to any of the + parties who would receive the covered work from you, a discriminatory + patent license (a) in connection with copies of the covered work + conveyed by you (or copies made from those copies), or (b) primarily + for and in connection with specific products or compilations that + contain the covered work, unless you entered into that arrangement, + or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot convey a + covered work so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you may + not convey it at all. For example, if you agree to terms that obligate you + to collect a royalty for further conveying from those to whom you convey + the Program, the only way you could satisfy both those terms and this + License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a single + combined work, and to convey the resulting work. The terms of this + License will continue to apply to the part which is the covered work, + but the special requirements of the GNU Affero General Public License, + section 13, concerning interaction through a network will apply to the + combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of + the GNU General Public License from time to time. Such new versions will + be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU General + Public License "or any later version" applies to it, you have the + option of following the terms and conditions either of that numbered + version or of any later version published by the Free Software + Foundation. If the Program does not specify a version number of the + GNU General Public License, you may choose any version ever published + by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that proxy's + public statement of acceptance of a version permanently authorizes you + to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely approximates + an absolute waiver of all civil liability in connection with the + Program, unless a warranty or assumption of liability accompanies a + copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + diff --git a/libterminal/ansiq_README.md b/libterminal/ansiq_README.md new file mode 100644 index 0000000..0527e9f --- /dev/null +++ b/libterminal/ansiq_README.md @@ -0,0 +1,27 @@ +# c-ansi-sequences +The ANSI escape sequences used for terminal display control wrapped in macros. Everything is very simple, you can pretty much understand what each macro does when looking at the `#define`s and comments. If you are intierested enough into this and want more documentation, open an issue and hope it'll reach me. + +## Examples +```c +#include +#include + +int main(void) +{ + printf(ANSIQ_SETFG_GREEN"Hello, world!"ANSIQ_GR_RESET"\n"); + return 0; +} +``` +The code above will print `Hello, world!`in green text color. If you want to compose more styles together, do this: +```c +printf(ANSIQ_GR_SEQ(ANSIQ_FG_WHITE ANSIQ_BG_BLUE + ANSIQ_TXT_BLD ANSIQ_TXT_ITL) + "White, bold, italic text on blue background"ANSIQ_GR_RESET"\n"); +``` +If you want to use the same style composition in multiple places, you csn wrap that in a macro: +```c +#define WARNING(text) \ + ANSIQ_GR_SEQ(ANSIQ_FG_YELLOW ANSIQ_TXT_UNL) \ + text ANSIQ_GR_RESET + printf(WARNING("This is a warning")"\n"); +``` diff --git a/libterminal/terminal_common.h b/libterminal/terminal_common.h new file mode 100644 index 0000000..2cbd494 --- /dev/null +++ b/libterminal/terminal_common.h @@ -0,0 +1,6 @@ +#ifndef ANSIQ_COMMON_H +#define ANSIQ_COMMON_H + +#define ANSIQ_ESC "\x1b" + +#endif /* ANSIQ_COMMON_H */ diff --git a/libterminal/terminal_cursor.h b/libterminal/terminal_cursor.h new file mode 100644 index 0000000..2536919 --- /dev/null +++ b/libterminal/terminal_cursor.h @@ -0,0 +1,31 @@ +#ifndef ANSIQ_CURSOR_H +#define ANSIQ_CURSOR_H +#include + +#define ANSIQ_CUR_HOME ANSIQ_ESC "[H" +#define ANSIQ_CUR_SET(l, c) ANSIQ_ESC "[" #l ";" #c "H" +#define ANSIQ_DYN_CUR_SET ANSIQ_CUR_SET (% d, % d) +#define ANSIQ_CUR_UP(n) ANSIQ_ESC "[" #n "A" +#define ANSIQ_DYN_CUR_UP ANSIQ_CUR_UP (% d) +#define ANSIQ_CUR_DOWN(n) ANSIQ_ESC "[" #n "B" +#define ANSIQ_DYN_CUR_DOWN ANSIQ_CUR_DOWN (% d) +#define ANSIQ_CUR_RIGHT(n) ANSIQ_ESC "[" #n "C" +#define ANSIQ_DYN_CUR_RIGHT ANSIQ_CUR_RIGHT (% d) +#define ANSIQ_CUR_LEFT(n) ANSIQ_ESC "[" #n "D" +#define ANSIQ_DYN_CUR_LEFT ANSIQ_CUR_LEFT (% d) +#define ANSIQ_CUR_UP_BEG(n) ANSIQ_ESC "[" #n "E" +#define ANSIQ_DYN_CUR_UP_BEG ANSIQ_CUR_UP_BEG (% d) +#define ANSIQ_CUR_DOWN_BEG(n) ANSIQ_ESC "[" #n "F" +#define ANSIQ_DYN_CUR_DOWN_BEG ANSIQ_CUR_DOWN_BEG (% d) +#define ANSIQ_CUR_SET_COL(n) ANSIQ_ESC "[" #n "G" +#define ANSIQ_DYN_CUR_SET_COL ANSIQ_CUR_SET_COL (% d) +#define ANSIQ_CUR_REQ_POS ANSIQ_ESC "[6n" +#define ANSIQ_CUR_MOVE_1UP ANSIQ_ESC "M" +#define ANSIQ_CUR_SAVE_DEC ANSIQ_ESC "7" +#define ANSIQ_CUR_RESTORE_DEC ANSIQ_ESC "8" +#define ANSIQ_CUR_SAVE_SCO ANSIQ_ESC "[s" +#define ANSIQ_CUR_RESTORE_SCO ANSIQ_ESC "[u" +#define ANSIQ_CUR_INVISIBLE ANSIQ_ESC "[?25l" +#define ANSIQ_CUR_VISIBLE ANSIQ_ESC "[?25h" + +#endif /* ANSIQ_CURSOR_H */ diff --git a/libterminal/terminal_graphics.h b/libterminal/terminal_graphics.h new file mode 100644 index 0000000..c0ed5e8 --- /dev/null +++ b/libterminal/terminal_graphics.h @@ -0,0 +1,126 @@ +#ifndef ANSIQ_GRAPHICS_H +#define ANSIQ_GRAPHICS_H +#include + +#define ANSIQ_GR_SEQ(s) ANSIQ_ESC "[" s "m" + +/* Text colors */ +#define ANSIQ_FG_BLACK ";30" +#define ANSIQ_FG_RED ";31" +#define ANSIQ_FG_GREEN ";32" +#define ANSIQ_FG_YELLOW ";33" +#define ANSIQ_FG_BLUE ";34" +#define ANSIQ_FG_MAGENTA ";35" +#define ANSIQ_FG_CYAN ";36" +#define ANSIQ_FG_WHITE ";37" + +#define __ANSIQ_SETFG_BLACK ANSIQ_ESC "[" ANSIQ_FG_BLACK +#define __ANSIQ_SETFG_RED ANSIQ_ESC "[" ANSIQ_FG_RED +#define __ANSIQ_SETFG_GREEN ANSIQ_ESC "[" ANSIQ_FG_GREEN +#define __ANSIQ_SETFG_YELLOW ANSIQ_ESC "[" ANSIQ_FG_YELLOW +#define __ANSIQ_SETFG_BLUE ANSIQ_ESC "[" ANSIQ_FG_BLUE +#define __ANSIQ_SETFG_MAGENTA ANSIQ_ESC "[" ANSIQ_FG_MAGENTA +#define __ANSIQ_SETFG_CYAN ANSIQ_ESC "[" ANSIQ_FG_CYAN +#define __ANSIQ_SETFG_WHITE ANSIQ_ESC "[" ANSIQ_FG_WHITE + +#define ANSIQ_SETFG_BLACK __ANSIQ_SETFG_BLACK "m" +#define ANSIQ_SETFG_RED __ANSIQ_SETFG_RED "m" +#define ANSIQ_SETFG_GREEN __ANSIQ_SETFG_GREEN "m" +#define ANSIQ_SETFG_YELLOW __ANSIQ_SETFG_YELLOW "m" +#define ANSIQ_SETFG_BLUE __ANSIQ_SETFG_BLUE "m" +#define ANSIQ_SETFG_MAGENTA __ANSIQ_SETFG_MAGENTA "m" +#define ANSIQ_SETFG_CYAN __ANSIQ_SETFG_CYAN "m" +#define ANSIQ_SETFG_WHITE __ANSIQ_SETFG_WHITE "m" + +#define ANSIQ_BRIGHT_FG(c) ANSIQ_FG_##c ";1" +#define ANSIQ_FG256(n) ";38;5;" #n +#define ANSIQ_SETBRIGHT_FG(c) ANSIQ_ESC "[" ANSIQ_BRIGHT_FG (c) "m" +#define ANSIQ_SETFG256(n) ANSIQ_ESC "[" ANSIQ_FG256 (n) "m" +#define ANSIQ_DYN_BRIGHT_FG ANSIQ_BRIGHT_FG (% d) +#define ANSIQ_DYN_FG256 ANSIQ_FG256 (% d) +#define ANSIQ_DYN_SETBRIGHT_FG ANSIQ_SETBRIGHT_FG (% d) +#define ANSIQ_DYN_SETFG256 ANSIQ_SETFG256 (% d) + +#define ANSIQ_FG_RGB(r, g, b) ";38;2;" #r ";" #g ";" #b +#define ANSIQ_SETFG_RGB(r, g, b) ANSIQ_ESC "[" ANSIQ_FG_RGB (r, g, b) "m" +#define ANSIQ_DYN_FG_RGB ANSIQ_FG_RGB (% d, % d, % d) +#define ANSIQ_DYN_SETFG_RGB ANSIQ_SETFG_RGB (% d, % d, % d) + +#define ANSIQ_FG_DEFAULT \ + ANSIQ_ESC "[39m" // Set the foreground + // color to the default one + // (i.e. reset foreground + // colof only) + +/* Background colors */ +#define ANSIQ_BG_BLACK ";40" +#define ANSIQ_BG_RED ";41" +#define ANSIQ_BG_GREEN ";42" +#define ANSIQ_BG_YELLOW ";43" +#define ANSIQ_BG_BLUE ";44" +#define ANSIQ_BG_MAGENTA ";45" +#define ANSIQ_BG_CYAN ";46" +#define ANSIQ_BG_WHITE ";47" + +#define __ANSIQ_SETBG_BLACK ANSIQ_ESC "[" ANSIQ_BG_BLACK +#define __ANSIQ_SETBG_RED ANSIQ_ESC "[" ANSIQ_BG_RED +#define __ANSIQ_SETBG_GREEN ANSIQ_ESC "[" ANSIQ_BG_GREEN +#define __ANSIQ_SETBG_YELLOW ANSIQ_ESC "[" ANSIQ_BG_YELLOW +#define __ANSIQ_SETBG_BLUE ANSIQ_ESC "[" ANSIQ_BG_BLUE +#define __ANSIQ_SETBG_MAGENTA ANSIQ_ESC "[" ANSIQ_BG_MAGENTA +#define __ANSIQ_SETBG_CYAN ANSIQ_ESC "[" ANSIQ_BG_CYAN +#define __ANSIQ_SETBG_WHITE ANSIQ_ESC "[" ANSIQ_BG_WHITE + +#define ANSIQ_SETBG_BLACK __ANSIQ_SETBG_BLACK "m" +#define ANSIQ_SETBG_RED __ANSIQ_SETBG_RED "m" +#define ANSIQ_SETBG_GREEN __ANSIQ_SETBG_GREEN "m" +#define ANSIQ_SETBG_YELLOW __ANSIQ_SETBG_YELLOW "m" +#define ANSIQ_SETBG_BLUE __ANSIQ_SETBG_BLUE "m" +#define ANSIQ_SETBG_MAGENTA __ANSIQ_SETBG_MAGENTA "m" +#define ANSIQ_SETBG_CYAN __ANSIQ_SETBG_CYAN "m" +#define ANSIQ_SETBG_WHITE __ANSIQ_SETBG_WHITE "m" + +#define ANSIQ_BRIGHT_BG(c) ANSIQ_BG_##c ";1" +#define ANSIQ_BG256(n) ";48;5;" #n +#define ANSIQ_SETBRIGHT_BG(c) ANSIQ_ESC "[" ANSIQ_BRIGHT_BG (c) "m" +#define ANSIQ_SETBG256(n) ANSIQ_ESC "[" ANSIQ_BG256 (n) "m" +#define ANSIQ_DYN_BRIGHT_BG ANSIQ_BRIGHT_BG (% d) +#define ANSIQ_DYN_BG256 ANSIQ_BG256 (% d) +#define ANSIQ_DYN_SETBRIGHT_BG ANSIQ_SETBRIGHT_BG (% d) +#define ANSIQ_DYN_SETBG256 ANSIQ_SETBG256 (% d) + +#define ANSIQ_BG_RGB(r, g, b) ";48;2;" #r ";" #g ";" #b +#define ANSIQ_SETBG_RGB(r, g, b) ANSIQ_ESC "[" ANSIQ_BG_RGB (r, g, b) "m" +#define ANSIQ_DYN_BG_RGB ANSIQ_BG_RGB (% d, % d, % d) +#define ANSIQ_DYN_SETBG_RGB ANSIQ_SETBG_RGB (% d, % d, % d) + +#define ANSIQ_BG_DEFAULT \ + ANSIQ_ESC "[49m" // Set the background color + // to the default one + // (i.e. reset background + // color only) + +/* Text styles */ +#define ANSIQ_TXT_BLD ";1" // bold +#define ANSIQ_TXT_DIM ";2" // dim/faint +#define ANSIQ_TXT_ITL ";3" // italic +#define ANSIQ_TXT_UNL ";4" // underline +#define ANSIQ_TXT_BLK ";5" // blinking mode (idk what it does) +#define ANSIQ_TXT_REV ";7" // reversed +#define ANSIQ_TXT_HID ";8" // hidden/invisible +#define ANSIQ_TXT_STK ";9" // strikethrough + +#define ANSIQ_SETTXT_BLD ANSIQ_ESC "[" ANSIQ_TXT_BLD "m" +#define ANSIQ_SETTXT_DIM ANSIQ_ESC "[" ANSIQ_TXT_DIM "m" +#define ANSIQ_SETTXT_ITL ANSIQ_ESC "[" ANSIQ_TXT_ITL "m" +#define ANSIQ_SETTXT_UNL ANSIQ_ESC "[" ANSIQ_TXT_UNL "m" +#define ANSIQ_SETTXT_BLK ANSIQ_ESC "[" ANSIQ_TXT_BLK "m" +#define ANSIQ_SETTXT_REV ANSIQ_ESC "[" ANSIQ_TXT_REV "m" +#define ANSIQ_SETTXT_HID ANSIQ_ESC "[" ANSIQ_TXT_HID "m" +#define ANSIQ_SETTXT_STK ANSIQ_ESC "[" ANSIQ_TXT_STK "m" + +#define ANSIQ_GR_RESET \ + ANSIQ_ESC "[" \ + "0m" + +#endif /* ANSIQ_GRAPHICS_H */ diff --git a/libterminal/terminal_screen.h b/libterminal/terminal_screen.h new file mode 100644 index 0000000..0c9430a --- /dev/null +++ b/libterminal/terminal_screen.h @@ -0,0 +1,15 @@ +#ifndef ANSIQ_SCREEN_H +#define ANSIQ_SCREEN_H +#include + +#define ANSIQ_SCR_CLR2END ANSIQ_ESC "[0J" // Clear to end of screen +#define ANSIQ_SCR_CLR2BEG ANSIQ_ESC "[1J" // Clear to begining of screen +#define ANSIQ_SCR_CLR_ALL ANSIQ_ESC "[2J" // Clear entire screen +#define ANSIQ_SCR_CLR_SAV ANSIQ_ESC "[3J" // Clear saved lines +#define ANSIQ_SCR_CLR2LEND ANSIQ_ESC "[0K" // Clear to end of line +#define ANSIQ_SCR_CLR2LBEG ANSIQ_ESC "[1K" // Clear to begining of line +#define ANSIQ_SCR_CLR_LINE ANSIQ_ESC "[1K" // Clear entire line +#define ANSIQ_SCR_RESTORE ANSIQ_ESC "[?47l" +#define ANSIQ_SCR_SAVE ANSIQ_ESC "[?47h" + +#endif /* ANSIQ_SCREEN_H */ diff --git a/make/dist.mk b/make/dist.mk index 09c384d..9dd7aaa 100644 --- a/make/dist.mk +++ b/make/dist.mk @@ -1,5 +1,5 @@ exe := $(foreach d,$(apps),$(wildcard $(d)/$(d))) -extra := init.cmd +extra := LICENSE.txt all_dist: mop3dist.tar diff --git a/make/libaux.mk b/make/libaux.mk new file mode 100644 index 0000000..24967b4 --- /dev/null +++ b/make/libaux.mk @@ -0,0 +1,16 @@ +all_libaux: + make -C libaux platform=$(platform) all + +all_compiledb_libaux: + bear --output libaux/compile_commands.json -- make -C libaux platform=$(platform) all + +clean_libaux: + make -C libaux platform=$(platform) clean + +format_libaux: + make -C libaux platform=$(platform) format + +docs_libaux: + make -C libaux platform=$(platform) docs + +.PHONY: all_libaux clean_libaux format_libaux docs_libaux all_compiledb_libaux