From 7f0fc08c05142f9f70cb05a4b1b1d6f0b1a3279b Mon Sep 17 00:00:00 2001 From: axtloss Date: Thu, 17 Oct 2024 02:39:27 +0200 Subject: Add strfmt --- doc/strfmt.3 | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/extlib.h | 71 +++++++++++++++++++++++++++++++++++++++++++ src/extstring.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ tests/driver.c | 12 ++++---- tests/test_strfmt.c | 52 ++++++++++++++++++++++++++++++++ tests/tests.c | 2 ++ 6 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 doc/strfmt.3 create mode 100644 tests/test_strfmt.c diff --git a/doc/strfmt.3 b/doc/strfmt.3 new file mode 100644 index 0000000..7beefd0 --- /dev/null +++ b/doc/strfmt.3 @@ -0,0 +1,86 @@ +'\" t +.\" Copyright 2024 axtlos (axtlos@disroot.org) +.\" +.\" SPDX-License-Identifier: BSD-3-Clause + +.TH strfmt 3 2024-10-17 "extlib" +.SH NAME +strfmt \- style a string through ansi escape codes +.SH LIBRARY +extlib extended standard library +.RI ( libextlib ", " \-lextlib ) +.SH SYNOPSIS +.nf +.B #include +.P +.BI "char *strfmt (char *s, struct style fmt);" +.P +.BI "struct style { int color, boolean background, int styles }" +.P +.fi +.SH DESCRIPTION +The +.BR strfmt () +function wraps +.I *s +in ANSI escape codes according to the style given in +.IR fmt . +Extlib contains convenience enums for the basic 16 colours +and the greyscale colours 232-255. But any number in the 1-255 range can be supplied. +The greyscale colours assume 256 colour support from the terminal, and may not work on every platform. +.fi +.SH RETURN VALUE +.BR strfmt () +returns a copy of the string +.I *s +wrapped in ANSI escape codes. +If +.I fmt +does not define any style (i.e +.BI "(struct style) { 0, false, 0 }" +), a copy of +.I *s +is returned. +.SH EXAMPLES +The following code will write the text "meow" in different styles. +.\" SRC BEGIN (strfmt.c) +.EX +#include +#include +#include +#include +\& +int +main (void) +{ + // Red text, with no extra styles + char *red_text = strfmt ("meow", (struct style) { RED, false, 0 }); + puts (red_text); + free (red_text); +\& + // Blue, bold and italic text + char *blue_bi_text = strfmt ("meow", (struct style) { RED, false, BOLD|ITALIC }); + puts (blue_bi_text); + free (blue_bi_text); +\& + // Regular text on a magenta background + char *magenta_bg_text = strfmt ("meow", (struct style) { MAGENTA, true, 0 }); + puts (magenta_bg_text); + free (magenta_bg_text); +\& + // Gray, bold, italic, underlined, blinking, strikethrough text + char *gray_text = strfmt ("meow", (struct style) { GRAY23, false, BOLD|ITALIC|UNDERLINE|BLINKING }); + puts (gray_text); + free (gray_text); +\& + // Regular text, with no color or style + char *text = strfmt ("meow", (struct style) { 0, false, 0 }); + puts (text); + free (text); +\& + exit (EXIT_SUCCESS); +} +.SH AUTHORS +This manual page was written by Rose +.IR . +.\" SRC END diff --git a/src/extlib.h b/src/extlib.h index 03bb53b..c3d342a 100644 --- a/src/extlib.h +++ b/src/extlib.h @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef USE_SECURE_MEM #define free(x) error - use free_secure @@ -76,3 +77,73 @@ float min (float v, float min_v); /// Limit a value between min and max float cap (float v, float min_v, float max_v); + +#ifndef STYLESDEF +/// Original 16 terminal colors +enum colors16 { + BLACK, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + WHITE, + GRAY, + BRIGHTRED, + BRIGHTGREEN, + BRIGHTYELLOW, + BRIGHTBLUE, + BRIGHTMAGENTA, + BRIGHTCYAN, + BRIGHTWHITE +}; + +enum grayscale { + GRAY1 = 232, + GRAY2, + GRAY3, + GRAY4, + GRAY5, + GRAY6, + GRAY7, + GRAY8, + GRAY9, + GRAY10, + GRAY11, + GRAY12, + GRAY13, + GRAY14, + GRAY15, + GRAY16, + GRAY17, + GRAY18, + GRAY19, + GRAY20, + GRAY21, + GRAY22, + GRAY23, + GRAY24 +}; + +enum effects { + BOLD = 1, + DIM = 2, + ITALIC = 4, + UNDERLINE = 8, + BLINKING = 16, + INVERSE = 32, + HIDDEN = 64, + STRIKETHROUGH = 128 +}; + +struct style { + int color; + bool background; + int styles; +}; +#define STYLESDEF +#endif + +/// Format a string with ANSI escape codes +char *strfmt (char *s, struct style); diff --git a/src/extstring.c b/src/extstring.c index b69b1f7..685db4f 100644 --- a/src/extstring.c +++ b/src/extstring.c @@ -8,6 +8,17 @@ #include #include "extlib.h" +enum gfxnums { + num_BOLD = 1, + num_DIM, + num_ITALIC, + num_UNDERLINE, + num_BLINKING, + num_INVERSE, + num_HIDDEN, + num_STRIKETHROUGH, +}; + char * strlwr (char *s) { @@ -122,3 +133,67 @@ join_str (char **s, size_t len, char *delim) return ret; } + +// all freed strings in this function dont contain confidential data +// so free_secure isnt necessary +#undef free +void +append_gfxsqnc (char **escape_sqnc, size_t *sqnc_size, int gfx_num) +{ + size_t gfxbase_len = strlen ("\e[XXm")+1; + char *tmp_sqnc = malloc (*sqnc_size + gfxbase_len); + *sqnc_size += gfxbase_len; + sprintf (tmp_sqnc, "%s\e[%dm", *escape_sqnc, gfx_num); + free (*escape_sqnc); + *escape_sqnc = tmp_sqnc; +} + +char * +strfmt (char *s, struct style fmt) +{ + size_t gfxbase_len = strlen ("\e[XXm")+1; + size_t sqnc_size = strlen ("\e[0m") + strlen (s)+1; + char *escape_sqnc = strdup (""); + if (fmt.color > 0) { + char *clrbase = malloc (strlen ("\e[38;5;m")+4); + sprintf (clrbase, "\e[%d;5;%dm", fmt.background ? 48 : 38, fmt.color); + char *tmp_sqnc = malloc (sqnc_size + strlen (clrbase)); + sqnc_size += strlen (clrbase); + sprintf (tmp_sqnc, "%s%s", escape_sqnc, clrbase); + free (escape_sqnc); + escape_sqnc = tmp_sqnc; + free (clrbase); + } + + if ((fmt.styles & BOLD)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_BOLD); + + if ((fmt.styles & DIM)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_DIM); + + if ((fmt.styles & ITALIC)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_ITALIC); + + if ((fmt.styles & UNDERLINE)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_UNDERLINE); + + if ((fmt.styles & BLINKING)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_BLINKING); + + if ((fmt.styles & INVERSE)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_INVERSE); + + if ((fmt.styles & HIDDEN)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_HIDDEN); + + if ((fmt.styles & STRIKETHROUGH)) + append_gfxsqnc (&escape_sqnc, &sqnc_size, num_STRIKETHROUGH); + + if (strlen (escape_sqnc) <= 2) { + free (escape_sqnc); + return strdup (s); + } + + sprintf (escape_sqnc, "%s%s\e[0m", escape_sqnc, s); + return escape_sqnc; +} diff --git a/tests/driver.c b/tests/driver.c index d3739b5..4d998b8 100644 --- a/tests/driver.c +++ b/tests/driver.c @@ -6,9 +6,8 @@ #include "../src/extlib.h" #include "test_driver.h" -const char* success = "\033[1;32m"; -const char* fail = " \033[1;31m"; -const char *reset = "\033[0m"; +char* success; +char* fail; int tests_count; struct test_t** registered_tests; @@ -19,9 +18,9 @@ test_runner (struct test_t *test, int n) printf ("Test Case %d: %s -- %s\n", n, test->test_name, test->test_desc); int test_result = test->test_func(); if (test_result == 0) - printf ("Test Case %d: %s -- %sSUCCESS%s\n\n", n, test->test_name, success, reset); + printf ("Test Case %d: %s -- %s\n\n", n, test->test_name, success); else - printf ("Test Case %d: %s -- %sFAIL %d%s\n\n", n, test->test_name, fail, test_result, reset); + printf ("Test Case %d: %s -- %s %d\n\n", n, test->test_name, fail, test_result); } void @@ -36,6 +35,9 @@ register_test (struct test_t *test) int main (int argc, char *argv[]) { + success = strfmt("SUCCESS", (struct style) {.color = GREEN, .background = false, .styles = BOLD|UNDERLINE}); + fail = strfmt("FAIL", (struct style) {.color = RED, .background = false, .styles = BOLD|UNDERLINE}); + registered_tests = malloc (8); tests_entrypoint(); for (int i = 0; i < tests_count; i++) diff --git a/tests/test_strfmt.c b/tests/test_strfmt.c new file mode 100644 index 0000000..c8ab08e --- /dev/null +++ b/tests/test_strfmt.c @@ -0,0 +1,52 @@ +#include +#define USE_SECURE_MEM +#include "../src/extlib.h" +#include "stdbool.h" + +int +test_strfmt () +{ + int errno = 0; + char *test_string_1 = strfmt ("meow", (struct style) {0, false, BOLD}); + printf ("\e[1mtest_strfmt\e[0m:: No Color, Bold: %s\n", test_string_1); + if (strcmp (test_string_1, "\e[1mmeow\e[0m") != 0) + errno = 1; + + char *test_string_2 = strfmt ("meow", (struct style) {RED, false, 0}); + printf ("\e[1mtest_strfmt\e[0m:: Red, No style: %s\n", test_string_2); + if (strcmp (test_string_2, "\e[38;5;1mmeow\e[0m") != 0) + errno = 2; + + char *test_string_3 = strfmt ("meow", (struct style) {RED, false, BOLD}); + printf ("\e[1mtest_strfmt\e[0m:: Red, Bold: %s\n", test_string_3); + if (strcmp (test_string_3, "\e[38;5;1m\e[1mmeow\e[0m") != 0) + errno = 3; + + char *test_string_4 = strfmt ("meow", (struct style) {RED, true, 0}); + printf ("\e[1mtest_strfmt\e[0m:: Red background, No style: %s\n", test_string_4); + if (strcmp (test_string_4, "\e[48;5;1mmeow\e[0m") != 0) + errno = 4; + + char *test_string_5 = strfmt ("meow", (struct style) {RED, true, BOLD}); + printf ("\e[1mtest_strfmt\e[0m:: Red background, Bold: %s\n", test_string_5); + if (strcmp (test_string_5, "\e[48;5;1m\e[1mmeow\e[0m") != 0) + errno = 5; + + free_secure ((void **) &test_string_1, strlen (test_string_1)); + free_secure ((void **) &test_string_2, strlen (test_string_2)); + free_secure ((void **) &test_string_3, strlen (test_string_3)); + free_secure ((void **) &test_string_4, strlen (test_string_4)); + free_secure ((void **) &test_string_5, strlen (test_string_5)); + return errno; +} + +struct test_t* +test_strfmt_t () +{ + struct test_t* test = malloc (sizeof (struct test_t)); + test->test_func=test_strfmt; + test->test_name="test_strfmt"; + test->test_desc="strfmt"; + return test; +} + diff --git a/tests/tests.c b/tests/tests.c index 73dd1af..295cbc8 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -7,6 +7,7 @@ #include "test_replace_str.c" #include "test_strlwr_strupr.c" #include "test_trim.c" +#include "test_strfmt.c" inline void tests_entrypoint () @@ -19,4 +20,5 @@ tests_entrypoint () register_test (test_replace_str_t ()); register_test (test_strlwr_strupr_t ()); register_test (test_trim_t ()); + register_test (test_strfmt_t ()); } -- cgit v1.2.3