diff --git a/term-test/printing.c b/term-test/printing.c index 86e3066..91724bd 100644 --- a/term-test/printing.c +++ b/term-test/printing.c @@ -1,8 +1,9 @@ #include +#include -#define F_GREEN "\033[92m" -#define F_YELLOW "\033[93m" -#define F_RESET "\033[0m" +#define F_GREEN "[green]" +#define F_YELLOW "[yellow]" +#define F_RESET "[reset]" static const char *text = F_YELLOW "But I must " F_GREEN "explain " F_YELLOW "to you " F_GREEN @@ -51,7 +52,15 @@ static const char *text2 int main(void) { - b_paragraph_format format = {}; + const char *s = "[magenta,uline]Hello, [bright]world![reset]"; + b_fputs(s, stdout); + fputc('\n', stdout); + + b_string *str = b_string_create_from_cstr(s); + size_t len = b_string_get_size(str, B_STRLEN_IGNORE_MOD); + printf("length = %zu\n", len); + + b_paragraph_format format = { 0 }; format.p_left_margin = 5; format.p_right_margin = 5; format.p_flags = B_PARAGRAPH_DOUBLE_LINE_BREAK; @@ -61,5 +70,6 @@ int main(void) b_i("An informational message\n\nWith multiple lines"); b_warn("A warning message\nWith multiple lines"); b_err("An error message\nWith multiple lines"); + return 0; } diff --git a/term/paragraph.c b/term/paragraph.c index e7961a8..99367ad 100644 --- a/term/paragraph.c +++ b/term/paragraph.c @@ -38,7 +38,24 @@ static unsigned int extract_line( unsigned int i; for (i = 0; start[i]; i++) { if (start[i] == '\033') { - while (!isalpha(start[i++])) { + while (start[i] && !isalpha(start[i])) { + delta++; + i++; + } + + delta++; + } + + if (start[i] == '[') { + if (start[i + 1] == '[') { + delta++; + i++; + + continue; + } + + while (start[i] && start[i] != ']') { + i++; delta++; } @@ -137,7 +154,8 @@ static b_status print_paragraph_tty( indent(format, fp, left_margin, false); } - b_fprintf(fp, "%s\n", b_string_ptr(line)); + b_fputs(b_string_ptr(line), fp); + fputc('\n', fp); need_indent = true; } diff --git a/term/print.c b/term/print.c index 7c70b7e..b79d65d 100644 --- a/term/print.c +++ b/term/print.c @@ -1,38 +1,238 @@ #include "print.h" #include +#include #include #include +#include #include -#define F_BLACK "\033[30m" -#define F_RED "\033[91m" -#define F_YEL "\033[93m" -#define F_CYN "\033[96m" +#define F_BLACK "[black]" +#define F_RED "[red]" +#define F_YEL "[yellow]" +#define F_CYN "[cyan]" -#define F_BLACK_BOLD "\033[1;30m" -#define F_RED_BOLD "\033[1;91m" -#define F_YEL_BOLD "\033[1;93m" -#define F_CYN_BOLD "\033[1;96m" +#define F_BLACK_BOLD "[bold,black]" +#define F_RED_BOLD "[bold,red]" +#define F_YEL_BOLD "[bold,yellow]" +#define F_CYN_BOLD "[bold,cyan]" -#define F_BLACK_BG "\033[40m" -#define F_RED_BG "\033[101m" -#define F_YEL_BG "\033[103m" -#define F_CYN_BG "\033[106m" +#define F_BLACK_BG "[bg_black]" +#define F_RED_BG "[bg_reg]" +#define F_YEL_BG "[bg_yellow]" +#define F_CYN_BG "[bg_cyan]" -#define F_RESET "\033[0m" +#define F_RESET "[reset]" typedef b_status (*print_function)(FILE *fp, const char *s); +#define MOD_HASH_BLACK 0x4b5dd0abbc6fc1e4 +#define MOD_HASH_RED 0x89e9be1960f4c21c +#define MOD_HASH_GREEN 0x0f40f029637fecbc +#define MOD_HASH_YELLOW 0x8346a574925e75a9 +#define MOD_HASH_BLUE 0xc5ccd29bc2dda64d +#define MOD_HASH_MAGENTA 0x6c90e772edbc8708 +#define MOD_HASH_CYAN 0x70ae2e90c1bce27a +#define MOD_HASH_WHITE 0xced973885856e206 + +#define MOD_HASH_BG_BLACK 0xd87a8f2d9d394432 +#define MOD_HASH_BG_RED 0x145b1e4366c7d7aa +#define MOD_HASH_BG_GREEN 0xa00b8541d3b1e55a +#define MOD_HASH_BG_YELLOW 0x98b030fd86e3b3cf +#define MOD_HASH_BG_BLUE 0xa15529109506b5df +#define MOD_HASH_BG_MAGENTA 0x86dbda99bcc86222 +#define MOD_HASH_BG_CYAN 0xf16a3104cf61a098 +#define MOD_HASH_BG_WHITE 0x3408c46ab5836674 + +#define MOD_HASH_BOLD 0xcd32ea9bc6b26ff6 +#define MOD_HASH_BRIGHT 0xb5cca637f5a2b385 +#define MOD_HASH_ULINE 0x141fe741e9f8c22a +#define MOD_HASH_ITALIC 0x69d5e5f057d8992d +#define MOD_HASH_INVERT 0xab4ab85ddd6232e1 +#define MOD_HASH_RESET 0x0f136ff2c086b760 + +#define COMPARE_MOD_NAME(ss, sd, hs, hd) ((hs) == (hd) && !strcmp(ss, sd)) + b_status b_term_get_dimensions(FILE *fp, unsigned int *w, unsigned int *h) { return z__b_stream_dimensions(fp, w, h) == 0 ? B_SUCCESS : B_ERR_NOT_SUPPORTED; } +static void apply_modifier(FILE *fp, const char *modifier, unsigned int *mod_flagp) +{ + uint64_t mod_hash = b_hash_string(modifier); + unsigned int mod_flags = *mod_flagp; + + if (COMPARE_MOD_NAME(modifier, "black", mod_hash, MOD_HASH_BLACK)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BLACK; + } + + if (COMPARE_MOD_NAME(modifier, "red", mod_hash, MOD_HASH_RED)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_RED; + } + + if (COMPARE_MOD_NAME(modifier, "green", mod_hash, MOD_HASH_GREEN)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_GREEN; + } + + if (COMPARE_MOD_NAME(modifier, "yellow", mod_hash, MOD_HASH_YELLOW)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_GREEN; + } + + if (COMPARE_MOD_NAME(modifier, "blue", mod_hash, MOD_HASH_BLUE)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "magenta", mod_hash, MOD_HASH_MAGENTA)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "cyan", mod_hash, MOD_HASH_CYAN)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_GREEN | Z__B_STREAM_MOD_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "white", mod_hash, MOD_HASH_WHITE)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_FG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_GREEN | Z__B_STREAM_MOD_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "bg_black", mod_hash, MOD_HASH_BG_BLACK)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_BLACK; + } + + if (COMPARE_MOD_NAME(modifier, "bg_red", mod_hash, MOD_HASH_BG_RED)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_RED; + } + + if (COMPARE_MOD_NAME(modifier, "bg_green", mod_hash, MOD_HASH_BG_GREEN)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_GREEN; + } + + if (COMPARE_MOD_NAME(modifier, "bg_yellow", mod_hash, MOD_HASH_BG_YELLOW)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_GREEN; + } + + if (COMPARE_MOD_NAME(modifier, "bg_blue", mod_hash, MOD_HASH_BG_BLUE)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "bg_magenta", mod_hash, MOD_HASH_BG_MAGENTA)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "bg_cyan", mod_hash, MOD_HASH_BG_CYAN)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_GREEN | Z__B_STREAM_MOD_BG_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "bg_white", mod_hash, MOD_HASH_BG_WHITE)) { + mod_flags = Z__B_STREAM_MOD_CLEAR_BG_COLOUR(mod_flags); + mod_flags |= Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_GREEN | Z__B_STREAM_MOD_BG_BLUE; + } + + if (COMPARE_MOD_NAME(modifier, "bold", mod_hash, MOD_HASH_BOLD)) { + mod_flags |= Z__B_STREAM_MOD_BOLD; + } + + if (COMPARE_MOD_NAME(modifier, "bright", mod_hash, MOD_HASH_BRIGHT)) { + mod_flags |= Z__B_STREAM_MOD_BRIGHT; + } + + if (COMPARE_MOD_NAME(modifier, "uline", mod_hash, MOD_HASH_ULINE)) { + mod_flags |= Z__B_STREAM_MOD_ULINE; + } + + if (COMPARE_MOD_NAME(modifier, "italic", mod_hash, MOD_HASH_ITALIC)) { + mod_flags |= Z__B_STREAM_MOD_ITALIC; + } + + if (COMPARE_MOD_NAME(modifier, "invert", mod_hash, MOD_HASH_INVERT)) { + mod_flags |= Z__B_STREAM_MOD_INVERT; + } + + if (COMPARE_MOD_NAME(modifier, "reset", mod_hash, MOD_HASH_RESET)) { + mod_flags = Z__B_STREAM_MOD_RESET; + } + + *mod_flagp = mod_flags; +} + static int print(FILE *fp, const char *str) { - return fputs(str, fp); + int len = 0; + char ctrl_buf[128]; + unsigned int ctrl_len = 0; + + for (size_t i = 0; str[i]; ) { + char c = str[i++]; + + if (c != '[') { + fputc(c, fp); + len++; + continue; + } + + c = str[i++]; + + if (c == '[') { + fputc(c, fp); + len++; + continue; + } + + ctrl_buf[ctrl_len++] = c; + unsigned int mod_flags = 0; + + while (true) { + c = str[i++]; + + if (isspace(c)) { + continue; + } + + if (c == '\0') { + break; + } + + if (c == ']') { + ctrl_buf[ctrl_len] = '\0'; + apply_modifier(fp, ctrl_buf, &mod_flags); + ctrl_len = 0; + ctrl_buf[0] = '\0'; + break; + } + + if (c == ',') { + ctrl_buf[ctrl_len] = '\0'; + apply_modifier(fp, ctrl_buf, &mod_flags); + ctrl_len = 0; + ctrl_buf[0] = '\0'; + continue; + } + + if (ctrl_len < sizeof ctrl_buf - 1) { + ctrl_buf[ctrl_len++] = c; + } + } + + z__b_stream_set_modifier(fp, mod_flags); + } + + return len; } static int print_no_ansi(FILE *fp, const char *str) @@ -61,7 +261,7 @@ static b_status print_normal(FILE *fp, const char *s) static b_status print_i(FILE *fp, const char *s) { - b_paragraph_format format = {}; + b_paragraph_format format = {0}; format.p_left_margin = 5; format.p_right_margin = 5; format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE @@ -76,7 +276,7 @@ static b_status print_i(FILE *fp, const char *s) static b_status print_warn(FILE *fp, const char *s) { - b_paragraph_format format = {}; + b_paragraph_format format = {0}; format.p_left_margin = 5; format.p_right_margin = 5; format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE @@ -91,7 +291,7 @@ static b_status print_warn(FILE *fp, const char *s) static b_status print_err(FILE *fp, const char *s) { - b_paragraph_format format = {}; + b_paragraph_format format = {0}; format.p_left_margin = 5; format.p_right_margin = 5; format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE diff --git a/term/print.h b/term/print.h index fa90b05..188b753 100644 --- a/term/print.h +++ b/term/print.h @@ -5,9 +5,41 @@ #include #include +enum { + Z__B_STREAM_MOD_BLACK = 0x1, + Z__B_STREAM_MOD_RED = 0x2, + Z__B_STREAM_MOD_GREEN = 0x4, + Z__B_STREAM_MOD_BLUE = 0x8, + + Z__B_STREAM_MOD_BG_BLACK = 0x10, + Z__B_STREAM_MOD_BG_RED = 0x20, + Z__B_STREAM_MOD_BG_GREEN = 0x40, + Z__B_STREAM_MOD_BG_BLUE = 0x80, + + Z__B_STREAM_MOD_BOLD = 0x100, + Z__B_STREAM_MOD_BRIGHT = 0x200, + Z__B_STREAM_MOD_ITALIC = 0x400, + Z__B_STREAM_MOD_ULINE = 0x800, + Z__B_STREAM_MOD_INVERT = 0x1000, + + Z__B_STREAM_MOD_RESET = 0x2000, +}; + +#define Z__B_STREAM_MOD_GET_FG_COLOUR(x) \ + ((x) & (Z__B_STREAM_MOD_BLACK | Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_GREEN | Z__B_STREAM_MOD_BLUE)) +#define Z__B_STREAM_MOD_CLEAR_FG_COLOUR(x) \ + ((x) & ~(Z__B_STREAM_MOD_BLACK | Z__B_STREAM_MOD_RED | Z__B_STREAM_MOD_GREEN | Z__B_STREAM_MOD_BLUE)) + +#define Z__B_STREAM_MOD_GET_BG_COLOUR(x) \ + ((x) & (Z__B_STREAM_MOD_BG_BLACK | Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_GREEN | Z__B_STREAM_MOD_BG_BLUE)) +#define Z__B_STREAM_MOD_CLEAR_BG_COLOUR(x) \ + ((x) & ~(Z__B_STREAM_MOD_BG_BLACK | Z__B_STREAM_MOD_BG_RED | Z__B_STREAM_MOD_BG_GREEN | Z__B_STREAM_MOD_BG_BLUE)) + + BLUE_API int z__b_stream_is_tty(FILE *fp); BLUE_API int z__b_stream_dimensions(FILE *fp, unsigned int *w, unsigned int *h); BLUE_API int z__b_stream_cursorpos( FILE *in, FILE *out, unsigned int *x, unsigned int *y); +BLUE_API int z__b_stream_set_modifier(FILE *fp, unsigned int mod); #endif diff --git a/term/sys/windows/print.c b/term/sys/windows/print.c index b46566e..c479f79 100644 --- a/term/sys/windows/print.c +++ b/term/sys/windows/print.c @@ -1,6 +1,7 @@ #include #include #include +#include "../../print.h" int z__b_stream_is_tty(FILE *fp) { @@ -22,11 +23,11 @@ int z__b_stream_dimensions(FILE *fp, unsigned int *w, unsigned int *h) } if (w) { - *w = csbi.dwMaximumWindowSize.X; + *w = csbi.srWindow.Right - csbi.srWindow.Left; } if (h) { - *h = csbi.dwMaximumWindowSize.Y; + *h = csbi.srWindow.Bottom - csbi.srWindow.Top; } return 0; @@ -37,6 +38,7 @@ int z__b_stream_cursorpos(FILE *in, FILE *out, unsigned int *x, unsigned int *y) CONSOLE_SCREEN_BUFFER_INFO csbi; HANDLE console = (HANDLE)_get_osfhandle(fileno(in)); BOOL status = GetConsoleScreenBufferInfo(console, &csbi); + if (status == FALSE) { return -1; @@ -52,3 +54,63 @@ int z__b_stream_cursorpos(FILE *in, FILE *out, unsigned int *x, unsigned int *y) return 0; } + +#define APPLY_FLAG(mod, attrib, x, y) \ + if ((mod) & (x)) { \ + (attrib) |= (y); \ + } + +#define APPLY_FLAG_X(mod, attrib, x, y) \ + if ((mod) & (x)) { \ + (attrib) |= (y); \ + } else { \ + (attrib) &= ~(y); \ + } + +int z__b_stream_set_modifier(FILE *fp, unsigned int mod) +{ + WORD attrib = 0; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + HANDLE console = (HANDLE)_get_osfhandle(fileno(fp)); + BOOL status = GetConsoleScreenBufferInfo(console, &csbi); + + if (status == FALSE) { + return -1; + } + + attrib = csbi.wAttributes; + + if (mod & Z__B_STREAM_MOD_RESET) { + attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + SetConsoleTextAttribute(console, attrib); + return 0; + } + + if (Z__B_STREAM_MOD_GET_FG_COLOUR(mod) != 0) { + if (mod & Z__B_STREAM_MOD_BLACK) { + attrib &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + } else { + APPLY_FLAG_X(mod, attrib, Z__B_STREAM_MOD_RED, FOREGROUND_RED); + APPLY_FLAG_X(mod, attrib, Z__B_STREAM_MOD_GREEN, FOREGROUND_GREEN); + APPLY_FLAG_X(mod, attrib, Z__B_STREAM_MOD_BLUE, FOREGROUND_BLUE); + } + } + + if (Z__B_STREAM_MOD_GET_BG_COLOUR(mod) != 0) { + if (mod & Z__B_STREAM_MOD_BG_BLACK) { + attrib &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + } else { + APPLY_FLAG_X(mod, attrib, Z__B_STREAM_MOD_BG_RED, BACKGROUND_RED); + APPLY_FLAG_X(mod, attrib, Z__B_STREAM_MOD_BG_GREEN, BACKGROUND_GREEN); + APPLY_FLAG_X(mod, attrib, Z__B_STREAM_MOD_BG_BLUE, BACKGROUND_BLUE); + } + } + + APPLY_FLAG(mod, attrib, Z__B_STREAM_MOD_ULINE, COMMON_LVB_UNDERSCORE); + APPLY_FLAG(mod, attrib, Z__B_STREAM_MOD_INVERT, COMMON_LVB_REVERSE_VIDEO); + APPLY_FLAG(mod, attrib, Z__B_STREAM_MOD_BRIGHT | Z__B_STREAM_MOD_BOLD, FOREGROUND_INTENSITY); + + SetConsoleTextAttribute(console, attrib); + return 0; +}