Unify Ragel color_ops build with production source

This commit is contained in:
Stephen Dennis 2026-03-18 06:32:10 -06:00
parent 1cdd58a9f1
commit 92d68aa2d9
5 changed files with 46 additions and 2936 deletions

View file

@ -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

View file

@ -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 */

File diff suppressed because it is too large Load diff

View file

@ -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. */

View file

@ -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);