mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Unify Ragel color_ops build with production source
This commit is contained in:
parent
1cdd58a9f1
commit
92d68aa2d9
5 changed files with 46 additions and 2936 deletions
|
|
@ -5,16 +5,21 @@ CC = gcc
|
|||
CFLAGS = -O2 -Wall -Wextra -Wno-implicit-fallthrough -std=c11 -g
|
||||
RAGEL = ragel
|
||||
RFLAGS = -G2 -C
|
||||
INCLUDES = -I../mux/include -I.
|
||||
|
||||
COLOR_OBJS = color_ops.o unicode_tables.o test_harness.o tables_ascii.o
|
||||
COLOR_OBJS = color_ops.o unicode_tables.o test_harness.o tables_ascii.o \
|
||||
tables_extra.o co_console_width.o co_nearest_color.o
|
||||
TRIG_OBJS = trigger_match.o test_trigger.o
|
||||
COLOR_OPS_RL = ../mux/lib/color_ops.rl
|
||||
ASCII_TABLES = ../mux/lib/utf8tables.cpp
|
||||
EXTRA_TABLES = ../mux/rv64/src/unicode_tables.c
|
||||
|
||||
.PHONY: all clean test test-color test-trigger
|
||||
|
||||
all: test_harness test_trigger_bin
|
||||
|
||||
# Ragel .rl -> .c
|
||||
color_ops.c: color_ops.rl
|
||||
color_ops.c: $(COLOR_OPS_RL) ../mux/include/color_ops.h ../mux/include/unicode_tables_c.h
|
||||
$(RAGEL) $(RFLAGS) -o $@ $<
|
||||
|
||||
trigger_match.c: trigger_match.rl
|
||||
|
|
@ -27,17 +32,35 @@ test_trigger_bin: $(TRIG_OBJS)
|
|||
$(CC) $(CFLAGS) -o $@ $(TRIG_OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
|
||||
|
||||
tables_ascii.o: ../tests/color_ops/tables_ascii.c
|
||||
$(CC) $(CFLAGS) -c -o $@ ../tests/color_ops/tables_ascii.c
|
||||
tables_ascii.c: $(ASCII_TABLES)
|
||||
@echo "/* Auto-extracted tr_ascii DFA tables from utf8tables.cpp */" > $@
|
||||
sed -n '/^const.*tr_ascii_itt/,/^};/p' $(ASCII_TABLES) >> $@
|
||||
sed -n '/^const.*tr_ascii_sot/,/^};/p' $(ASCII_TABLES) >> $@
|
||||
sed -n '/^const.*tr_ascii_sbt/,/^};/p' $(ASCII_TABLES) >> $@
|
||||
|
||||
tables_extra.c: $(EXTRA_TABLES)
|
||||
@echo "/* Auto-extracted GCB/width/extpict tables from rv64/src/unicode_tables.c */" > $@
|
||||
@echo "#include <stddef.h>" >> $@
|
||||
@echo "#include \"unicode_tables_c.h\"" >> $@
|
||||
sed -n '/^const.*tr_widths_itt/,$$p' $(EXTRA_TABLES) >> $@
|
||||
|
||||
test_harness.o: test_harness.c color_ops.h
|
||||
color_ops.o: color_ops.c color_ops.h unicode_tables.h
|
||||
unicode_tables.o: unicode_tables.c unicode_tables.h
|
||||
tables_extra.o: tables_extra.c ../mux/include/unicode_tables_c.h
|
||||
co_console_width.o: ../tests/color_ops/co_console_width.c
|
||||
co_nearest_color.o: ../tests/color_ops/co_nearest_color.c color_ops.h
|
||||
trigger_match.o: trigger_match.c trigger_match.h
|
||||
test_trigger.o: test_trigger.c trigger_match.h
|
||||
|
||||
co_console_width.o: ../tests/color_ops/co_console_width.c
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ ../tests/color_ops/co_console_width.c
|
||||
|
||||
co_nearest_color.o: ../tests/color_ops/co_nearest_color.c color_ops.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ ../tests/color_ops/co_nearest_color.c
|
||||
|
||||
test: test-color test-trigger
|
||||
|
||||
test-color: test_harness
|
||||
|
|
@ -47,5 +70,5 @@ test-trigger: test_trigger_bin
|
|||
./test_trigger_bin
|
||||
|
||||
clean:
|
||||
rm -f $(COLOR_OBJS) $(TRIG_OBJS) color_ops.c trigger_match.c \
|
||||
rm -f $(COLOR_OBJS) $(TRIG_OBJS) color_ops.c tables_ascii.c tables_extra.c trigger_match.c \
|
||||
test_harness test_trigger_bin
|
||||
|
|
|
|||
|
|
@ -1,656 +1,12 @@
|
|||
/*
|
||||
* color_ops.h — Ragel -G2 string mutation primitives for PUA-colored UTF-8.
|
||||
* Compatibility wrapper for the standalone Ragel harness.
|
||||
*
|
||||
* TinyMUX encodes color as Unicode Private Use Area code points inline
|
||||
* in UTF-8 strings:
|
||||
*
|
||||
* BMP PUA (3-byte UTF-8):
|
||||
* U+F500 reset
|
||||
* U+F501 intense
|
||||
* U+F504 underline
|
||||
* U+F505 blink
|
||||
* U+F507 inverse
|
||||
* U+F600-F6FF 256 foreground XTERM indexed colors
|
||||
* U+F700-F7FF 256 background XTERM indexed colors
|
||||
*
|
||||
* Supplementary PUA (4-byte UTF-8, Plane 15):
|
||||
* U+F0000-F0FFF FG CP1: (R high nibble << 8) | G
|
||||
* U+F1000-F1FFF FG CP2: (R low nibble << 8) | B
|
||||
* U+F2000-F2FFF BG CP1: (R high nibble << 8) | G
|
||||
* U+F3000-F3FFF BG CP2: (R low nibble << 8) | B
|
||||
*
|
||||
* A color annotation is 1-3 code points: a base (3 bytes BMP PUA),
|
||||
* optionally followed by exactly 2 SMP code points (4 bytes each).
|
||||
* 24-bit color = base + 2 SMP = always 11 bytes per layer.
|
||||
*
|
||||
* These routines operate directly on the PUA-inline UTF-8 representation,
|
||||
* in a single pass, without stripping/re-inserting color metadata.
|
||||
* The single source of truth lives under mux/include/.
|
||||
*/
|
||||
|
||||
#ifndef COLOR_OPS_H
|
||||
#define COLOR_OPS_H
|
||||
#ifndef RAGEL_COLOR_OPS_WRAPPER_H
|
||||
#define RAGEL_COLOR_OPS_WRAPPER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "../mux/include/color_ops.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* LBUF_SIZE matches TinyMUX. */
|
||||
#define LBUF_SIZE 8000
|
||||
|
||||
/*
|
||||
* co_visible_length — Count visible code points (skipping color PUA).
|
||||
*
|
||||
* Returns the number of non-color Unicode code points in the string.
|
||||
* This counts code points in a null-terminated UTF-8 string.
|
||||
*/
|
||||
size_t co_visible_length(const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_skip_color — Advance past any color PUA code points at current position.
|
||||
*
|
||||
* Returns pointer to the first non-color byte at or after p.
|
||||
* Does not advance past the end (p + len).
|
||||
*/
|
||||
const unsigned char *co_skip_color(const unsigned char *p,
|
||||
const unsigned char *pe);
|
||||
|
||||
/*
|
||||
* co_visible_advance — Advance past exactly n visible code points.
|
||||
*
|
||||
* Skips color code points transparently. Returns pointer to the byte
|
||||
* after the nth visible code point (or pe if fewer than n remain).
|
||||
* If out_count is non-NULL, stores the actual number advanced.
|
||||
*/
|
||||
const unsigned char *co_visible_advance(const unsigned char *p,
|
||||
const unsigned char *pe,
|
||||
size_t n,
|
||||
size_t *out_count);
|
||||
|
||||
/*
|
||||
* co_copy_visible — Copy up to n visible code points, preserving color.
|
||||
*
|
||||
* Copies bytes from [p, pe) to out, including any interleaved color
|
||||
* code points, but counting only visible code points toward the limit.
|
||||
* Returns the number of bytes written to out.
|
||||
* out must have room for at least LBUF_SIZE bytes.
|
||||
*/
|
||||
size_t co_copy_visible(unsigned char *out, const unsigned char *p,
|
||||
const unsigned char *pe, size_t n);
|
||||
|
||||
/*
|
||||
* co_find_delim — Find first occurrence of single-byte delimiter,
|
||||
* skipping color PUA code points.
|
||||
*
|
||||
* Returns pointer to the delimiter byte, or NULL if not found.
|
||||
* Only matches against visible bytes (never inside color sequences).
|
||||
*/
|
||||
const unsigned char *co_find_delim(const unsigned char *p,
|
||||
const unsigned char *pe,
|
||||
unsigned char delim);
|
||||
|
||||
/* ---- Stage 1: Word and substring operations ---- */
|
||||
|
||||
/*
|
||||
* co_strip_color — Copy string with all color PUA removed.
|
||||
*
|
||||
* Returns bytes written to out (visible content only).
|
||||
*/
|
||||
size_t co_strip_color(unsigned char *out, const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_words_count — Count words separated by delimiter, skipping color.
|
||||
*
|
||||
* Matches MUX words() behavior: consecutive delimiters do NOT create
|
||||
* empty words (they are compressed).
|
||||
*/
|
||||
size_t co_words_count(const unsigned char *p, size_t len,
|
||||
unsigned char delim);
|
||||
|
||||
/*
|
||||
* co_first — Extract the first word before delimiter, preserving color.
|
||||
*
|
||||
* Copies all bytes (including color) up to but not including the first
|
||||
* delimiter occurrence. Returns bytes written to out.
|
||||
*/
|
||||
size_t co_first(unsigned char *out, const unsigned char *p, size_t len,
|
||||
unsigned char delim);
|
||||
|
||||
/*
|
||||
* co_rest — Everything after the first word, preserving color.
|
||||
*
|
||||
* Skips past the first delimiter and copies the remainder.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_rest(unsigned char *out, const unsigned char *p, size_t len,
|
||||
unsigned char delim);
|
||||
|
||||
/*
|
||||
* co_last — Extract the last word after the final delimiter.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_last(unsigned char *out, const unsigned char *p, size_t len,
|
||||
unsigned char delim);
|
||||
|
||||
/*
|
||||
* co_extract — Extract words iFirst..iLast (1-based), preserving color.
|
||||
*
|
||||
* MUX extract() semantics: words are 1-based, delimiters are compressed.
|
||||
* Copies the extracted words (with their original color) separated by
|
||||
* the output delimiter. Returns bytes written to out.
|
||||
*/
|
||||
size_t co_extract(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t iFirst, size_t nWords,
|
||||
unsigned char delim, unsigned char osep);
|
||||
|
||||
/*
|
||||
* co_left — First n visible code points, preserving color.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_left(unsigned char *out,
|
||||
const unsigned char *p, size_t len, size_t n);
|
||||
|
||||
/*
|
||||
* co_right — Last n visible code points, preserving color.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_right(unsigned char *out,
|
||||
const unsigned char *p, size_t len, size_t n);
|
||||
|
||||
/*
|
||||
* co_trim — Trim leading/trailing characters, preserving color.
|
||||
*
|
||||
* trim_flags: 1 = trim left, 2 = trim right, 3 = both.
|
||||
* Trims any visible code point whose first byte matches trim_char.
|
||||
* If trim_char is 0, trims ASCII whitespace (0x20, 0x09).
|
||||
*/
|
||||
size_t co_trim(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
unsigned char trim_char, int trim_flags);
|
||||
|
||||
/*
|
||||
* co_search — Find first occurrence of a visible substring pattern,
|
||||
* skipping color in both haystack and needle.
|
||||
*
|
||||
* Returns pointer into haystack where the match begins (at a visible
|
||||
* code point), or NULL if not found. Only compares visible bytes.
|
||||
*/
|
||||
const unsigned char *co_search(const unsigned char *haystack, size_t hlen,
|
||||
const unsigned char *needle, size_t nlen);
|
||||
|
||||
/*
|
||||
* co_split_words — Split PUA-encoded string by multi-char delimiter.
|
||||
*
|
||||
* Populates word_starts[] and word_ends[] with byte offsets into data.
|
||||
* Space delimiter uses compress semantics; non-space is exact.
|
||||
* Returns number of words found.
|
||||
*/
|
||||
size_t co_split_words(const unsigned char *data, size_t len,
|
||||
const unsigned char *sep, size_t sep_len,
|
||||
size_t *word_starts, size_t *word_ends,
|
||||
size_t max_words);
|
||||
|
||||
/*
|
||||
* co_trim_pattern — Trim repeating multi-byte pattern from edges.
|
||||
*
|
||||
* Pattern is matched cyclically at the raw byte level.
|
||||
* trim_flags: 1 = left, 2 = right, 3 = both.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_trim_pattern(unsigned char *out,
|
||||
const unsigned char *data, size_t len,
|
||||
const unsigned char *pattern, size_t plen,
|
||||
int trim_flags);
|
||||
|
||||
/*
|
||||
* co_compress_str — Compress runs of a multi-char separator into one.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_compress_str(unsigned char *out,
|
||||
const unsigned char *data, size_t len,
|
||||
const unsigned char *sep, size_t sep_len);
|
||||
|
||||
/* ---- Stage 2: Transforms, edit, reverse ---- */
|
||||
|
||||
/*
|
||||
* co_toupper — Convert visible code points to uppercase, preserving color.
|
||||
*
|
||||
* Full Unicode case mapping via DFA tables from the utf/ pipeline
|
||||
* (~1477 code points). Output may be longer or shorter than input
|
||||
* (e.g., ß → SS). Color PUA code points are passed through unchanged.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_toupper(unsigned char *out, const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_tolower — Convert visible code points to lowercase, preserving color.
|
||||
*
|
||||
* Full Unicode case mapping via DFA tables (~1460 code points).
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_tolower(unsigned char *out, const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_totitle — Title-case first visible code point, preserving color.
|
||||
*
|
||||
* Full Unicode title-case mapping via DFA tables (~1481 code points).
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_totitle(unsigned char *out, const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_reverse — Reverse visible code points, preserving color attachment.
|
||||
*
|
||||
* Each visible code point's preceding color annotation stays attached
|
||||
* to it after reversal. Leading color (before any visible char) stays
|
||||
* at the front.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_reverse(unsigned char *out, const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_edit — Find-and-replace, color-aware.
|
||||
*
|
||||
* Replaces all non-overlapping occurrences of pattern with replacement.
|
||||
* Pattern matching skips color (same as co_search).
|
||||
* Color in the haystack around the matched region is preserved;
|
||||
* the replacement is inserted without color.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_edit(unsigned char *out,
|
||||
const unsigned char *str, size_t slen,
|
||||
const unsigned char *from, size_t flen,
|
||||
const unsigned char *to, size_t tlen);
|
||||
|
||||
/*
|
||||
* co_transform — Character mapping (tr/translate).
|
||||
*
|
||||
* For each visible code point in str, if it appears in from_set at
|
||||
* position i, replace it with the code point at position i in to_set.
|
||||
* Color is preserved. from_set and to_set must have the same number
|
||||
* of visible code points.
|
||||
*
|
||||
* ASCII fast path: if both sets are pure ASCII, uses 256-byte lookup.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_transform(unsigned char *out,
|
||||
const unsigned char *str, size_t slen,
|
||||
const unsigned char *from_set, size_t flen,
|
||||
const unsigned char *to_set, size_t tlen);
|
||||
|
||||
/*
|
||||
* co_compress — Collapse runs of a character.
|
||||
*
|
||||
* Reduces consecutive runs of trim_char to a single occurrence.
|
||||
* If trim_char is 0, compresses runs of ASCII whitespace.
|
||||
* Color is preserved (color between compressed chars is dropped).
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_compress(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
unsigned char compress_char);
|
||||
|
||||
/* ---- Stage 3: Position, substring, padding ---- */
|
||||
|
||||
/*
|
||||
* co_mid — Substring by visible position, preserving color.
|
||||
*
|
||||
* MUX mid() semantics: iStart is 0-based visible index, nCount is the
|
||||
* number of visible code points to extract. Color interleaved in the
|
||||
* extracted range is preserved. Color preceding the range is included
|
||||
* if it immediately precedes the first extracted visible code point.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_mid(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t iStart, size_t nCount);
|
||||
|
||||
/*
|
||||
* co_pos — Find first visible-substring position (1-based).
|
||||
*
|
||||
* MUX pos() semantics: returns the 1-based visible code point index
|
||||
* where needle first appears in haystack. Returns 0 if not found.
|
||||
* Color is skipped in both haystack and needle during matching.
|
||||
*/
|
||||
size_t co_pos(const unsigned char *haystack, size_t hlen,
|
||||
const unsigned char *needle, size_t nlen);
|
||||
|
||||
/*
|
||||
* co_lpos — Find all occurrences of a single-byte pattern.
|
||||
*
|
||||
* MUX lpos() semantics: writes space-separated 0-based visible
|
||||
* positions of every occurrence of the pattern byte.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_lpos(unsigned char *out,
|
||||
const unsigned char *haystack, size_t hlen,
|
||||
unsigned char pattern);
|
||||
|
||||
/*
|
||||
* co_member — Find word in word-list (1-based).
|
||||
*
|
||||
* MUX member() semantics: searches the delimited word list for an
|
||||
* exact match of target. Returns the 1-based word index, or 0 if
|
||||
* not found. Comparison is on visible content (color stripped).
|
||||
*/
|
||||
size_t co_member(const unsigned char *target, size_t tlen,
|
||||
const unsigned char *list, size_t llen,
|
||||
unsigned char delim);
|
||||
|
||||
/*
|
||||
* co_console_width — Column width of a single visible code point.
|
||||
*
|
||||
* Returns 0 for combining marks, 2 for fullwidth/CJK, 1 otherwise.
|
||||
* Wraps ConsoleWidth() from stringutil.cpp with C linkage.
|
||||
*/
|
||||
int co_console_width(const unsigned char *pCodePoint);
|
||||
|
||||
/*
|
||||
* co_visual_width — Total display column width of a PUA-colored string.
|
||||
*
|
||||
* Skips color PUA codes, sums co_console_width() for visible chars.
|
||||
* Accounts for fullwidth (2 columns) and zero-width (0 columns).
|
||||
*/
|
||||
size_t co_visual_width(const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_copy_columns — Copy up to n display columns, preserving color.
|
||||
*
|
||||
* Like co_copy_visible but counts display columns instead of code points.
|
||||
* A fullwidth character counts as 2 columns. Stops before emitting a
|
||||
* character that would exceed the column limit.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_copy_columns(unsigned char *out, const unsigned char *p,
|
||||
const unsigned char *pe, size_t ncols);
|
||||
|
||||
/*
|
||||
* co_center — Center string with padding, preserving color.
|
||||
*
|
||||
* Pads string to exactly 'width' display columns, centered.
|
||||
* fill/fill_len is the fill pattern (cycled, phase-continuous).
|
||||
* If fill_len is 0, uses space. bTrunc: truncate if wider.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_center(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t width,
|
||||
const unsigned char *fill, size_t fill_len,
|
||||
int bTrunc);
|
||||
|
||||
/*
|
||||
* co_ljust — Left-justify (right-pad) to width columns, preserving color.
|
||||
*
|
||||
* bTrunc: if true, truncate input to width if wider.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_ljust(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t width,
|
||||
const unsigned char *fill, size_t fill_len,
|
||||
int bTrunc);
|
||||
|
||||
/*
|
||||
* co_rjust — Right-justify (left-pad) to width columns, preserving color.
|
||||
*
|
||||
* bTrunc: if true, truncate input to width if wider.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_rjust(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t width,
|
||||
const unsigned char *fill, size_t fill_len,
|
||||
int bTrunc);
|
||||
|
||||
/*
|
||||
* co_repeat — Repeat string n times, preserving color.
|
||||
*
|
||||
* Returns bytes written to out, or 0 if result would exceed LBUF_SIZE.
|
||||
*/
|
||||
size_t co_repeat(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t count);
|
||||
|
||||
/* ---- Stage 4: Delete, splice, insert, sort, set operations ---- */
|
||||
|
||||
/*
|
||||
* co_delete — Delete visible code points by position, preserving color.
|
||||
*
|
||||
* MUX delete() semantics: iStart is 0-based visible index, nCount is
|
||||
* the number of visible code points to delete. Color interleaved in
|
||||
* the deleted range is dropped. Color before and after is preserved.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_delete(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
size_t iStart, size_t nCount);
|
||||
|
||||
/*
|
||||
* co_splice — Word-level splice: replace matching words from list2.
|
||||
*
|
||||
* MUX splice() semantics: for each word in list1, if it matches
|
||||
* search_word, output the corresponding word from list2 instead.
|
||||
* list1 and list2 must have the same number of words.
|
||||
* Returns bytes written to out, or 0 on error (mismatched lengths).
|
||||
*/
|
||||
size_t co_splice(unsigned char *out,
|
||||
const unsigned char *list1, size_t len1,
|
||||
const unsigned char *list2, size_t len2,
|
||||
const unsigned char *search, size_t slen,
|
||||
unsigned char delim, unsigned char osep);
|
||||
|
||||
/*
|
||||
* co_insert_word — Insert a word at a position in a word list.
|
||||
*
|
||||
* MUX insert() semantics: iPos is 1-based word position.
|
||||
* The new word is inserted before position iPos.
|
||||
* If iPos <= 0, inserts at the beginning.
|
||||
* If iPos > number of words, appends at the end.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_insert_word(unsigned char *out,
|
||||
const unsigned char *list, size_t llen,
|
||||
const unsigned char *word, size_t wlen,
|
||||
size_t iPos,
|
||||
unsigned char delim, unsigned char osep);
|
||||
|
||||
/*
|
||||
* co_sort_words — Sort a word list, preserving color per-word.
|
||||
*
|
||||
* sort_type: 'a' = ASCII, 'i' = case-insensitive ASCII,
|
||||
* 'n' = numeric, 'd' = dbref.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_sort_words(unsigned char *out,
|
||||
const unsigned char *list, size_t llen,
|
||||
unsigned char delim, unsigned char osep,
|
||||
char sort_type);
|
||||
|
||||
/*
|
||||
* co_setunion — Set union of two word lists, sorted and deduplicated.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_setunion(unsigned char *out,
|
||||
const unsigned char *list1, size_t len1,
|
||||
const unsigned char *list2, size_t len2,
|
||||
unsigned char delim, unsigned char osep,
|
||||
char sort_type);
|
||||
|
||||
/*
|
||||
* co_setdiff — Set difference (list1 minus list2), sorted.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_setdiff(unsigned char *out,
|
||||
const unsigned char *list1, size_t len1,
|
||||
const unsigned char *list2, size_t len2,
|
||||
unsigned char delim, unsigned char osep,
|
||||
char sort_type);
|
||||
|
||||
/*
|
||||
* co_setinter — Set intersection of two word lists, sorted.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_setinter(unsigned char *out,
|
||||
const unsigned char *list1, size_t len1,
|
||||
const unsigned char *list2, size_t len2,
|
||||
unsigned char delim, unsigned char osep,
|
||||
char sort_type);
|
||||
|
||||
/* ---- Stage 5: Color collapse ---- */
|
||||
|
||||
/*
|
||||
* co_ColorState — Tracks the current color/attribute state.
|
||||
*
|
||||
* fg/bg: -1 = default (terminal default), 0-255 = XTERM palette index,
|
||||
* -2 = 24-bit RGB (use fg_r/g/b or bg_r/g/b).
|
||||
* Attributes: 0 = off, 1 = on.
|
||||
*/
|
||||
typedef struct {
|
||||
int16_t fg; /* -1=default, 0-255=indexed, -2=RGB */
|
||||
int16_t bg; /* -1=default, 0-255=indexed, -2=RGB */
|
||||
uint8_t fg_r, fg_g, fg_b;
|
||||
uint8_t bg_r, bg_g, bg_b;
|
||||
uint8_t intense, underline, blink, inverse;
|
||||
} co_ColorState;
|
||||
|
||||
/* The default (reset) state. */
|
||||
#define CO_CS_NORMAL ((co_ColorState){ -1, -1, 0,0,0, 0,0,0, 0,0,0,0 })
|
||||
|
||||
/* Convenience constructors for common states. */
|
||||
static inline co_ColorState co_cs_fg(int idx) {
|
||||
co_ColorState cs = CO_CS_NORMAL;
|
||||
cs.fg = (int16_t)idx;
|
||||
return cs;
|
||||
}
|
||||
static inline co_ColorState co_cs_bg(int idx) {
|
||||
co_ColorState cs = CO_CS_NORMAL;
|
||||
cs.bg = (int16_t)idx;
|
||||
return cs;
|
||||
}
|
||||
|
||||
/* Compare two color states for equality. */
|
||||
static inline int co_cs_equal(const co_ColorState *a, const co_ColorState *b) {
|
||||
return a->fg == b->fg && a->bg == b->bg
|
||||
&& a->fg_r == b->fg_r && a->fg_g == b->fg_g && a->fg_b == b->fg_b
|
||||
&& a->bg_r == b->bg_r && a->bg_g == b->bg_g && a->bg_b == b->bg_b
|
||||
&& a->intense == b->intense && a->underline == b->underline
|
||||
&& a->blink == b->blink && a->inverse == b->inverse;
|
||||
}
|
||||
|
||||
/*
|
||||
* co_collapse_color — Eliminate redundant PUA color sequences.
|
||||
*
|
||||
* Scans input PUA-colored UTF-8. For each visible code point, computes
|
||||
* the net color state from all preceding PUA codes, then emits only the
|
||||
* minimal PUA transition from the previously emitted state.
|
||||
*
|
||||
* Example: FG_RED + FG_GREEN + "A" → FG_GREEN + "A" (FG_RED dropped).
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_collapse_color(unsigned char *out,
|
||||
const unsigned char *p, size_t len);
|
||||
|
||||
/*
|
||||
* co_apply_color — Prepend a ColorState to a string as PUA bytes.
|
||||
*
|
||||
* Emits the minimal PUA sequence to transition from CO_CS_NORMAL
|
||||
* to the given state, then copies the string.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_apply_color(unsigned char *out,
|
||||
const unsigned char *p, size_t len,
|
||||
co_ColorState cs);
|
||||
|
||||
/*
|
||||
* co_merge — Positional character merge with color tracking.
|
||||
*
|
||||
* MUX merge() semantics: for each position, if strA's visible code point
|
||||
* matches search, replace with strB's code point and color.
|
||||
* Tracks absolute color state for both strings and emits correct
|
||||
* PUA transitions in the output.
|
||||
*
|
||||
* Returns bytes written to out, or 0 if visible lengths differ.
|
||||
*/
|
||||
size_t co_merge(unsigned char *out,
|
||||
const unsigned char *strA, size_t lenA,
|
||||
const unsigned char *strB, size_t lenB,
|
||||
const unsigned char *search, size_t slen);
|
||||
|
||||
/*
|
||||
* co_escape — Insert backslash before escape characters, preserving color.
|
||||
*
|
||||
* MUX escape() semantics: prepends '\' before the first visible character
|
||||
* and before any character in the set: $%(),;[\]^{}.
|
||||
* Color PUA codes are passed through unchanged.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_escape(unsigned char *out,
|
||||
const unsigned char *data, size_t len);
|
||||
|
||||
/* ---- Grapheme cluster operations ---- */
|
||||
|
||||
/*
|
||||
* co_cluster_count — Count Extended Grapheme Clusters in PUA-encoded string.
|
||||
*
|
||||
* Full UAX #29 grapheme segmentation (Unicode 16.0): handles combining
|
||||
* marks, Hangul jamo, emoji ZWJ sequences, regional indicators.
|
||||
* Color PUA code points are skipped (not counted as clusters).
|
||||
*/
|
||||
size_t co_cluster_count(const unsigned char *data, size_t len);
|
||||
|
||||
/*
|
||||
* co_cluster_advance — Advance past n grapheme clusters in PUA-encoded string.
|
||||
*
|
||||
* Returns pointer past the nth cluster (including any trailing color).
|
||||
* If out_count is non-NULL, stores the actual number of clusters advanced.
|
||||
*/
|
||||
const unsigned char *co_cluster_advance(const unsigned char *data,
|
||||
const unsigned char *pe,
|
||||
size_t n, size_t *out_count);
|
||||
|
||||
/*
|
||||
* co_mid_cluster — Substring by grapheme cluster position, preserving color.
|
||||
*
|
||||
* iStart is 0-based cluster index, nCount is number of clusters to extract.
|
||||
* Color interleaved in the extracted range is preserved.
|
||||
*
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_mid_cluster(unsigned char *out,
|
||||
const unsigned char *data, size_t len,
|
||||
size_t iStart, size_t nCount);
|
||||
|
||||
/*
|
||||
* co_delete_cluster — Delete grapheme clusters by position, preserving color.
|
||||
*
|
||||
* iStart is 0-based cluster index, nCount is number of clusters to delete.
|
||||
* Returns bytes written to out.
|
||||
*/
|
||||
size_t co_delete_cluster(unsigned char *out,
|
||||
const unsigned char *data, size_t len,
|
||||
size_t iStart, size_t nCount);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* COLOR_OPS_H */
|
||||
|
|
|
|||
2283
ragel/color_ops.rl
2283
ragel/color_ops.rl
File diff suppressed because it is too large
Load diff
|
|
@ -1121,8 +1121,8 @@ static void test_center(void)
|
|||
nb = co_center(out, (const unsigned char *)"hi", 2,
|
||||
8, (const unsigned char *)"=-", 2, 1);
|
||||
TEST("center(\"hi\", 8, \"=-\") -> cyclic fill");
|
||||
/* 3 left, 3 right: "=-=" + "hi" + "=-=" = 8 vis */
|
||||
if (nb == 8 && memcmp(out, "=-=hi=-=", 8) == 0) { PASS(); }
|
||||
/* Trailing fill keeps phase with the left padding + content width. */
|
||||
if (nb == 8 && memcmp(out, "=-=hi-=-", 8) == 0) { PASS(); }
|
||||
else { FAIL("got \"%.*s\" (%zu)", (int)nb, out, nb); }
|
||||
|
||||
/* Truncation: string wider than width. */
|
||||
|
|
|
|||
|
|
@ -992,6 +992,12 @@ static void test_justify(void) {
|
|||
check_buf("co_center", "'hello' w=11", out, r,
|
||||
(const unsigned char *)" hello ", 11);
|
||||
|
||||
/* Multi-char fill keeps phase across the content width. */
|
||||
r = co_center(out, (const unsigned char *)"hi", 2, 8,
|
||||
(const unsigned char *)"=-", 2, 1);
|
||||
check_buf("co_center", "'hi' w=8 fill='=-' phase-continuing", out, r,
|
||||
(const unsigned char *)"=-=hi-=-", 8);
|
||||
|
||||
/* Custom fill char. */
|
||||
r = co_ljust(out, (const unsigned char *)"hi", 2, 6,
|
||||
(const unsigned char *)"-", 1, 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue