Previously theme parsing included strlen(), even though we could
have retrieved the length directly from lua, and strdup()/free()
because we needed to split the string which required inserting a 0
for termination. This was all energy wasting busy work since all
the information we actually needed was contained in the original
string.
This commit adds ~100 lines of code because I have provided my own
implementation of some functions that we were previously provided
by the C-runtime. This code will be reused later to simplify lots
of other code in sam.c, map.c, vis-lua.c, and likely more. I can't
say if this will be a net reduction of code in vis but I can
guarantee that it will be a net reduction in total lines of code
running on the users machine - C-runtime implementations are often
massive over-complicated messes (even musl).
---
Makefile | 1 +
main.c | 2 +-
sam.c | 3 +-
ui-terminal-curses.c | 7 +--
ui-terminal-vt100.c | 19 +++----
ui-terminal.c | 123 +++++++++++++++++++++----------------------
ui.h | 2 +-
util.c | 71 +++++++++++++++++++++++++
util.h | 27 +++++++++-
vis-core.h | 3 +-
vis-lua.c | 8 +--
vis-subprocess.c | 3 +-
vis.c | 2 +-
13 files changed, 184 insertions(+), 87 deletions(-)
create mode 100644 util.c
diff --git a/Makefile b/Makefile
index a2f1df3..3d3cd23 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,7 @@ SRC = array.c \
text-util.c \
text.c \
ui-terminal.c \
+ util.c \
view.c \
vis-lua.c \
vis-marks.c \
diff --git a/main.c b/main.c
index 3e9d9ab..a915c72 100644
--- a/main.c
+++ b/main.c
@@ -10,13 +10,13 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include "util.h"
#include "ui.h"
#include "vis.h"
#include "vis-lua.h"
#include "text-util.h"
#include "text-motions.h"
#include "text-objects.h"
-#include "util.h"
#include "libutf.h"
#include "array.h"
#include "buffer.h"
diff --git a/sam.c b/sam.c
index e5aead3..7250ab6 100644
--- a/sam.c
+++ b/sam.c
@@ -24,6 +24,8 @@
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
+
+#include "util.h"
#include "sam.h"
#include "vis-core.h"
#include "buffer.h"
@@ -31,7 +33,6 @@
#include "text-motions.h"
#include "text-objects.h"
#include "text-regex.h"
-#include "util.h"
#define MAX_ARGV 8
diff --git a/ui-terminal-curses.c b/ui-terminal-curses.c
index a2aa7ec..779caff 100644
--- a/ui-terminal-curses.c
+++ b/ui-terminal-curses.c
@@ -73,13 +73,13 @@ static void undo_palette(void)
}
/* Work out the nearest color from the 256 color set, or perhaps exactly. */
-static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b)
+static CellColor color_rgb(bool config_change_colors, u8 r, u8 g, u8 b)
{
static short color_clobber_idx = 0;
static uint32_t clobbering_colors[MAX_COLOR_CLOBBER];
if (change_colors == -1)
- change_colors = ui->vis->change_colors && can_change_color() && COLORS >= 256;
+ change_colors = config_change_colors && can_change_color() && COLORS >= 256;
if (change_colors) {
uint32_t hexrep = (r << 16) | (g << 8) | b;
for (short i = 0; i < MAX_COLOR_CLOBBER; ++i) {
@@ -151,7 +151,8 @@ static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b)
return i;
}
-static CellColor color_terminal(Ui *ui, uint8_t index) {
+static CellColor color_terminal(u8 index)
+{
return index;
}
diff --git a/ui-terminal-vt100.c b/ui-terminal-vt100.c
index 3c63c11..959be81 100644
--- a/ui-terminal-vt100.c
+++ b/ui-terminal-vt100.c
@@ -59,34 +59,31 @@
#define CELL_ATTR_DIM (1 << 5)
static inline bool cell_color_equal(CellColor c1, CellColor c2) {
- if (c1.index != (uint8_t)-1 || c2.index != (uint8_t)-1)
- return c1.index == c2.index;
- return c1.r == c2.r && c1.g == c2.g && c1.b == c2.b;
+ return c1.index == c2.index && c1.r == c2.r && c1.g == c2.g && c1.b == c2.b;
}
-static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b) {
- return (CellColor){ .r = r, .g = g, .b = b, .index = (uint8_t)-1 };
+static CellColor color_rgb(bool unused, u8 r, u8 g, u8 b) {
+ return (CellColor){ .r = r, .g = g, .b = b, .index = (u8)-1 };
}
-static CellColor color_terminal(Ui *ui, uint8_t index) {
+static CellColor color_terminal(u8 index) {
return (CellColor){ .r = 0, .g = 0, .b = 0, .index = index };
}
-
static void output(const char *data, size_t len) {
write(STDERR_FILENO, data, len);
}
-static void output_literal(const char *data) {
- output(data, strlen(data));
+static void output_s8(s8 msg) {
+ output((char *)msg.data, msg.len);
}
static void screen_alternate(bool alternate) {
- output_literal(alternate ? "\x1b[?1049h" : "\x1b[0m" "\x1b[?1049l" "\x1b[0m" );
+ output_s8(alternate ? s8("\x1b[?1049h") : s8("\x1b[0m" "\x1b[?1049l" "\x1b[0m"));
}
static void cursor_visible(bool visible) {
- output_literal(visible ? "\x1b[?25h" : "\x1b[?25l");
+ output_s8(visible ? s8("\x1b[?25h") : s8("\x1b[?25l"));
}
static void ui_term_backend_blit(Ui *tui) {
diff --git a/ui-terminal.c b/ui-terminal.c
index 7cfb377..801358a 100644
--- a/ui-terminal.c
+++ b/ui-terminal.c
@@ -1,9 +1,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-#include <strings.h>
-#include <limits.h>
-#include <ctype.h>
#include <locale.h>
#include <poll.h>
#include <sys/ioctl.h>
@@ -13,10 +10,10 @@
#include <termios.h>
#include <errno.h>
+#include "util.h"
#include "vis.h"
#include "vis-core.h"
#include "text.h"
-#include "util.h"
#include "text-util.h"
#ifndef DEBUG_UI
@@ -77,46 +74,52 @@ static void ui_window_move(Win *win, int x, int y) {
win->y = y;
}
-static bool color_fromstring(Ui *ui, CellColor *color, const char *s)
+static b32
+color_from_s8(Vis *vis, CellColor *color, s8 s)
{
- if (!s)
+ if (s.len <= 0)
return false;
- if (*s == '#' && strlen(s) == 7) {
- const char *cp;
- unsigned char r, g, b;
- for (cp = s + 1; isxdigit((unsigned char)*cp); cp++);
- if (*cp != '\0')
- return false;
- int n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b);
- if (n != 3)
- return false;
- *color = color_rgb(ui, r, g, b);
+
+ #if 0
+ /* TODO(rnp): this can fire before the default style gets set which can break
+ * it for some reason. */
+ if (s.data[0] == '#' && s.len > 7) {
+ vis_info_show(vis, "WARNING: style_define: ignoring alpha channel in: %*s",
+ (int)s.len, s.data);
+ s.len = 7;
+ }
+ #endif
+
+ if (s.data[0] == '#' && s.len == 7) {
+ u32 rgb = s8_hex_to_u32(s);
+ *color = color_rgb(vis->change_colors,
+ (rgb >> 16) & 0xFFu, (rgb >> 8) & 0xFFu, (rgb >> 0) & 0xFFu);
return true;
- } else if ('0' <= *s && *s <= '9') {
- int index = atoi(s);
+ } else if ('0' <= s.data[0] && s.data[0] <= '9') {
+ int index = s8_to_i64(s);
if (index <= 0 || index > 255)
return false;
- *color = color_terminal(ui, index);
+ *color = color_terminal(index);
return true;
}
static const struct {
- const char *name;
+ s8 name;
CellColor color;
} color_names[] = {
- { "black", CELL_COLOR_BLACK },
- { "red", CELL_COLOR_RED },
- { "green", CELL_COLOR_GREEN },
- { "yellow", CELL_COLOR_YELLOW },
- { "blue", CELL_COLOR_BLUE },
- { "magenta", CELL_COLOR_MAGENTA },
- { "cyan", CELL_COLOR_CYAN },
- { "white", CELL_COLOR_WHITE },
- { "default", CELL_COLOR_DEFAULT },
+ { s8("black"), CELL_COLOR_BLACK },
+ { s8("red"), CELL_COLOR_RED },
+ { s8("green"), CELL_COLOR_GREEN },
+ { s8("yellow"), CELL_COLOR_YELLOW },
+ { s8("blue"), CELL_COLOR_BLUE },
+ { s8("magenta"), CELL_COLOR_MAGENTA },
+ { s8("cyan"), CELL_COLOR_CYAN },
+ { s8("white"), CELL_COLOR_WHITE },
+ { s8("default"), CELL_COLOR_DEFAULT },
};
for (size_t i = 0; i < LENGTH(color_names); i++) {
- if (strcasecmp(color_names[i].name, s) == 0) {
+ if (s8_case_ignore_equal(color_names[i].name, s)) {
*color = color_names[i].color;
return true;
}
@@ -125,58 +128,54 @@ static bool color_fromstring(Ui *ui, CellColor *color, const char *s)
return false;
}
-bool ui_style_define(Win *win, int id, const char *style) {
+b32 ui_style_define(Win *win, int id, s8 style) {
Ui *tui = &win->vis->ui;
if (id >= UI_STYLE_MAX)
return false;
- if (!style)
- return true;
CellStyle cell_style = CELL_STYLE_DEFAULT;
- char *style_copy = strdup(style), *option = style_copy;
- while (option) {
- while (*option == ' ')
- option++;
- char *next = strchr(option, ',');
- if (next)
- *next++ = '\0';
- char *value = strchr(option, ':');
- if (value)
- for (*value++ = '\0'; *value == ' '; value++);
- if (!strcasecmp(option, "reverse")) {
+ s8 option = style;
+ b32 result = true;
+ while (option.len) {
+ s8 next, value;
+ option = s8_trim_space(option);
+ s8_split(option, ',', &option, &next);
+ s8_split(option, ':', &option, &value);
+ value = s8_trim_space(value);
+
+ if (s8_case_ignore_equal(option, s8("reverse"))) {
cell_style.attr |= CELL_ATTR_REVERSE;
- } else if (!strcasecmp(option, "notreverse")) {
+ } else if (s8_case_ignore_equal(option, s8("notreverse"))) {
cell_style.attr &= CELL_ATTR_REVERSE;
- } else if (!strcasecmp(option, "bold")) {
+ } else if (s8_case_ignore_equal(option, s8("bold"))) {
cell_style.attr |= CELL_ATTR_BOLD;
- } else if (!strcasecmp(option, "notbold")) {
+ } else if (s8_case_ignore_equal(option, s8("notbold"))) {
cell_style.attr &= ~CELL_ATTR_BOLD;
- } else if (!strcasecmp(option, "dim")) {
+ } else if (s8_case_ignore_equal(option, s8("dim"))) {
cell_style.attr |= CELL_ATTR_DIM;
- } else if (!strcasecmp(option, "notdim")) {
+ } else if (s8_case_ignore_equal(option, s8("notdim"))) {
cell_style.attr &= ~CELL_ATTR_DIM;
- } else if (!strcasecmp(option, "italics")) {
+ } else if (s8_case_ignore_equal(option, s8("italics"))) {
cell_style.attr |= CELL_ATTR_ITALIC;
- } else if (!strcasecmp(option, "notitalics")) {
+ } else if (s8_case_ignore_equal(option, s8("notitalics"))) {
cell_style.attr &= ~CELL_ATTR_ITALIC;
- } else if (!strcasecmp(option, "underlined")) {
+ } else if (s8_case_ignore_equal(option, s8("underlined"))) {
cell_style.attr |= CELL_ATTR_UNDERLINE;
- } else if (!strcasecmp(option, "notunderlined")) {
+ } else if (s8_case_ignore_equal(option, s8("notunderlined"))) {
cell_style.attr &= ~CELL_ATTR_UNDERLINE;
- } else if (!strcasecmp(option, "blink")) {
+ } else if (s8_case_ignore_equal(option, s8("blink"))) {
cell_style.attr |= CELL_ATTR_BLINK;
- } else if (!strcasecmp(option, "notblink")) {
+ } else if (s8_case_ignore_equal(option, s8("notblink"))) {
cell_style.attr &= ~CELL_ATTR_BLINK;
- } else if (!strcasecmp(option, "fore")) {
- color_fromstring(&win->vis->ui, &cell_style.fg, value);
- } else if (!strcasecmp(option, "back")) {
- color_fromstring(&win->vis->ui, &cell_style.bg, value);
+ } else if (s8_case_ignore_equal(option, s8("fore"))) {
+ result &= color_from_s8(win->vis, &cell_style.fg, value);
+ } else if (s8_case_ignore_equal(option, s8("back"))) {
+ result &= color_from_s8(win->vis, &cell_style.bg, value);
}
option = next;
}
- tui->styles[win->id * UI_STYLE_MAX + id] = cell_style;
- free(style_copy);
- return true;
+ if (result) tui->styles[win->id * UI_STYLE_MAX + id] = cell_style;
+ return result;
}
static void ui_draw_line(Ui *tui, int x, int y, char c, enum UiStyle style_id) {
diff --git a/ui.h b/ui.h
index 5680400..531af71 100644
--- a/ui.h
+++ b/ui.h
@@ -127,7 +127,7 @@ void ui_window_swap(Win *, Win *);
bool ui_getkey(Ui *, TermKeyKey *);
-bool ui_style_define(Win *win, int id, const char *style);
+b32 ui_style_define(Win *win, int id, s8 style);
bool ui_window_style_set_pos(Win *win, int x, int y, enum UiStyle id);
void ui_window_style_set(Win *win, Cell *cell, enum UiStyle id);
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..05cdc78
--- /dev/null
+++ b/util.c
@@ -0,0 +1,71 @@
+#include "util.h"
+
+s8 s8_trim_space(s8 s)
+{
+ while (s.len > 0 && ISSPACE(s.data[0])) { s.len--; s.data++; }
+ while (s.len > 0 && ISSPACE(s.data[s.len - 1])) s.len--;
+ return s;
+}
+
+void s8_split(s8 in, u8 c, s8 *lhs, s8 *rhs)
+{
+ ix i;
+ s8 s = in;
+
+ for (i = 0; i < s.len && s.data[i] != c; i++);
+ s.len = i;
+
+ if (lhs) *lhs = s;
+
+ if (rhs) {
+ i32 inc = (s.len == in.len) ? 0 : 1;
+ rhs->len = in.len - s.len - inc;
+ rhs->data = in.data + s.len + inc;
+ }
+}
+
+b32 s8_case_ignore_equal(s8 a, s8 b)
+{
+ b32 result = a.len == b.len;
+ for (ix i = 0; result && i < a.len; i++)
+ result &= (a.data[i] & ~0x20u) == (b.data[i] & ~0x20u);
+ return result;
+}
+
+i64 s8_to_i64(s8 s)
+{
+ i64 result = 0, sign = 1;
+ if (s.len && s.data[0] == '-') {
+ sign = -1;
+ s.data++;
+ s.len--;
+ }
+ for (ix i = 0; i < s.len; i++) {
+ if (!ISDIGIT(s.data[i]))
+ break;
+ result *= 10;
+ result += s.data[i] - '0';
+ }
+ return sign * result;
+}
+
+u32 s8_hex_to_u32(s8 s)
+{
+ u32 res = 0;
+ /* NOTE: clamp length to something valid for a u32 */
+ s.len = MIN(s.len, 8);
+ for (; s.len > 0; s.len--, s.data++) {
+ res <<= 4;
+ if (ISDIGIT(*s.data)) {
+ res |= *s.data - '0';
+ } else if (ISHEX(*s.data)) {
+ /* NOTE: convert to lowercase first then convert to value */
+ u8 byte = *s.data;
+ byte |= 0x20u;
+ res |= byte - 0x57u;
+ } else {
+ /* NOTE: do nothing (treat invalid value as 0) */
+ }
+ }
+ return res;
+}
diff --git a/util.h b/util.h
index 08a44a8..303597e 100644
--- a/util.h
+++ b/util.h
@@ -1,16 +1,32 @@
#ifndef UTIL_H
#define UTIL_H
-#include <stdint.h>
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#define LENGTH(x) ((int)(sizeof (x) / sizeof *(x)))
#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b))
+
/* is c the start of a utf8 sequence? */
#define ISUTF8(c) (((c)&0xC0)!=0x80)
#define ISASCII(ch) ((unsigned char)ch < 0x80)
+#define ISDIGIT(c) (BETWEEN((c), '0', '9'))
+#define ISHEX(c) (ISDIGIT((c)) || BETWEEN((c), 'a', 'f') || BETWEEN((c), 'A', 'F'))
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+
+typedef uint8_t u8;
+typedef int32_t i32;
+typedef uint32_t u32;
+typedef uint32_t b32;
+typedef int64_t i64;
+typedef ptrdiff_t ix;
+
+typedef struct { ix len; u8 *data; } s8;
+#define s8(s) (s8){.len = (ix)sizeof(s) - 1, .data = (u8 *)s}
#if GCC_VERSION>=5004000 || CLANG_VERSION>=4000000
#define addu __builtin_add_overflow
@@ -44,4 +60,13 @@ static void *memrchr(const void *m, int c, size_t n)
#define PATH_MAX 4096
#endif
+s8 s8_trim_space(s8);
+
+void s8_split(s8, u8 cut_byte, s8 *lhs, s8 *rhs);
+
+i64 s8_to_i64(s8);
+u32 s8_hex_to_u32(s8);
+
+b32 s8_case_ignore_equal(s8, s8);
+
#endif /* UTIL_H */
diff --git a/vis-core.h b/vis-core.h
index 182c537..6bcd3ce 100644
--- a/vis-core.h
+++ b/vis-core.h
@@ -2,6 +2,8 @@
#define VIS_CORE_H
#include <setjmp.h>
+#include "util.h"
+
#include "vis.h"
#include "sam.h"
#include "vis-lua.h"
@@ -10,7 +12,6 @@
#include "map.h"
#include "array.h"
#include "buffer.h"
-#include "util.h"
/* a mode contains a set of key bindings which are currently valid.
*
diff --git a/vis-lua.c b/vis-lua.c
index d593bda..86f4ecb 100644
--- a/vis-lua.c
+++ b/vis-lua.c
@@ -21,10 +21,10 @@
#include <sys/types.h>
#include <pwd.h>
+#include "util.h"
#include "vis-lua.h"
#include "vis-core.h"
#include "text-motions.h"
-#include "util.h"
#ifndef VIS_PATH
#define VIS_PATH "/usr/local/share/vis"
@@ -2015,9 +2015,9 @@ static int window_unmap(lua_State *L) {
static int window_style_define(lua_State *L) {
Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
enum UiStyle id = luaL_checkunsigned(L, 2);
- const char *style = luaL_checkstring(L, 3);
- bool ret = ui_style_define(win, id, style);
- lua_pushboolean(L, ret);
+ size_t len;
+ u8 *style = (u8 *)luaL_checklstring(L, 3, &len);
+ lua_pushboolean(L, ui_style_define(win, id, (s8){.len = len, .data = style}));
return 1;
}
diff --git a/vis-subprocess.c b/vis-subprocess.c
index e17d24f..8c181c5 100644
--- a/vis-subprocess.c
+++ b/vis-subprocess.c
@@ -4,9 +4,10 @@
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
+
+#include "util.h"
#include "vis-lua.h"
#include "vis-subprocess.h"
-#include "util.h"
/* Pool of information about currently running subprocesses */
static Process *process_pool;
diff --git a/vis.c b/vis.c
index c059a48..4c6e098 100644
--- a/vis.c
+++ b/vis.c
@@ -20,11 +20,11 @@
#include <libgen.h>
#include <termkey.h>
+#include "util.h"
#include "vis.h"
#include "text-util.h"
#include "text-motions.h"
#include "text-objects.h"
-#include "util.h"
#include "vis-core.h"
#include "sam.h"
#include "ui.h"
--
2.45.2