diff --git a/src/boxes.c b/src/boxes.c index 35c36c4..3f9e423 100644 --- a/src/boxes.c +++ b/src/boxes.c @@ -24,6 +24,7 @@ #include #include "boxes.h" +#include "bxstring.h" #include "cmdline.h" #include "discovery.h" #include "generate.h" diff --git a/src/remove.c b/src/remove.c index e253f3b..5e959c2 100644 --- a/src/remove.c +++ b/src/remove.c @@ -400,6 +400,9 @@ static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, s if (p != NULL && p < end_pos && is_blank_between(cur_pos, p)) { result = hmm(shapes_relevant, p + quality, i + (shapes_relevant[i].elastic ? 0 : 1), end_pos, 1, anchored_right); + if (result == 0 && shapes_relevant[i].elastic) { + result = hmm(shapes_relevant, p + quality, i + 1, end_pos, 1, anchored_right); + } break; } if (can_shorten_right == -1) { @@ -521,6 +524,9 @@ static int match_horiz_line(remove_ctx_t *ctx, int hside, size_t input_line_idx, continue; } ctx->comp_type = comp_type; + #ifdef DEBUG + fprintf(stderr, " Setting comparison type to: %s\n", comparison_name[comp_type]); + #endif shape_line_ctx_t *shapes_relevant = prepare_comp_shapes_horiz(hside, comp_type, shape_line_idx); debug_print_shapes_relevant(shapes_relevant); diff --git a/src/tools.c b/src/tools.c index e224d13..2140e78 100644 --- a/src/tools.c +++ b/src/tools.c @@ -566,6 +566,14 @@ int is_csi_reset(const uint32_t *csi) if (puc >= 0x40 && puc <= 0x7e) { return 1; } + else if ((puc == '1' && *rest == '0') + || (puc == '3' && *rest == '9') + || (puc == '4' && *rest == '9') + || (puc == '5' && *rest == '9') + || (puc == '7' && *rest == '5')) { + rest = u32_next(&puc, rest); + break; + } return 0; } break; diff --git a/src/tools.h b/src/tools.h index b3b7cfa..76b2808 100644 --- a/src/tools.h +++ b/src/tools.h @@ -156,10 +156,12 @@ size_t count_invisible_chars(const uint32_t *s, size_t *num_esc, char **ascii, s /** - * Determine whether the given sequence of characters is a CSI (also called "escape sequence") that resets all - * modifications, typically `ESC[0m`. - * @param csi a pointer into a zero-terminated UTF-32 string - * @returns 1 if true, 0 if false + * Determine if `csi` points at a CSI which can be considered a "reset" sequence. + * https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters + * We recognize `\x1b[0m`, and also 10, 39, 49, 59, and 75 instead of 0 as "resets", because they all serve to reset + * the terminal to some default. Especially, lolcat uses `\x1b[39m` as reset CSI. + * @param csi a string which might or might not be a CSI "reset" sequence (zero-terminated UTF-32 string) + * @return 1 if it was a CSI "reset", 0 otherwise */ int is_csi_reset(const uint32_t *csi); diff --git a/test/181_remove_box_ignore_invisible_all.txt b/test/181_remove_box_ignore_invisible_all.txt new file mode 100644 index 0000000..f718d56 --- /dev/null +++ b/test/181_remove_box_ignore_invisible_all.txt @@ -0,0 +1,28 @@ +:DESC +Remove a box consisting of unicode characters and ansi escape codes, even when the colors are all wrong (input box +has different colors than the box design). +Boxes shall recognize a box design by the visible characters, not by the colors. +This tests the comparison mode 'ignore_invisible_all'. + +:ARGS +--config 18x_remove_box_ignore_invisible_all.cfg --remove +:INPUT +         .-"```"-. +        /_______; \ +       (_________)\| +       / / 👁 👁 \ \(_) +      / ( \___/ ) \ +  _ooo\__\_____/__/____ + /                     \ +|     Hello World!      | + \_________________ooo_/ +      /           \ +     /:.:.:.:.:.:.:\ +         |  |  | +         \==|==/  jgs +         /-'Y'-\ +        (__/ \__) +:OUTPUT-FILTER +:EXPECTED +    Hello World! +:EOF diff --git a/test/182_remove_box_ignore_invisible_shape.txt b/test/182_remove_box_ignore_invisible_shape.txt new file mode 100644 index 0000000..5eaa5c2 --- /dev/null +++ b/test/182_remove_box_ignore_invisible_shape.txt @@ -0,0 +1,26 @@ +:DESC +In this test case, the box design is fully colored, but the input is monochrome. +This tests the comparison mode 'ignore_invisible_shape'. + +:ARGS +--config 18x_remove_box_ignore_invisible_all.cfg --design designA --remove +:INPUT + .-"```"-. + /_______; \ + (_________)\| + / / 👁 👁 \ \(_) + / ( \___/ ) \ + _ooo\__\_____/__/____ + / \ +| Hello World! | + \_________________ooo_/ + / \ + /:.:.:.:.:.:.:\ + | | | + \==|==/ jgs + /-'Y'-\ + (__/ \__) +:OUTPUT-FILTER +:EXPECTED + Hello World! +:EOF diff --git a/test/183_remove_box_ignore_invisible_input.txt b/test/183_remove_box_ignore_invisible_input.txt new file mode 100644 index 0000000..3da692a --- /dev/null +++ b/test/183_remove_box_ignore_invisible_input.txt @@ -0,0 +1,28 @@ +:DESC +In this test case, the box design is monochrome, but the input is colored. +This tests the comparison mode 'ignore_invisible_input'. + +:ARGS +--design capgirl --remove +:INPUT +         .-"```"-. +        /_______; \ +       (_________)\| +       / / a a \ \(_) +      / ( \___/ ) \ +  _ooo\__\_____/__/____ + /                     \ +|  Hello, world!  | +|  World, hello!  | + \_________________ooo_/ +      /           \ +     /:.:.:.:.:.:.:\ +         |  |  | +         \==|==/  jgs +         /-'Y'-\ +        (__/ \__) +:OUTPUT-FILTER +:EXPECTED + Hello, world! + World, hello! +:EOF diff --git a/test/18x_remove_box_ignore_invisible_all.cfg b/test/18x_remove_box_ignore_invisible_all.cfg new file mode 100644 index 0000000..7ba2b52 --- /dev/null +++ b/test/18x_remove_box_ignore_invisible_all.cfg @@ -0,0 +1,71 @@ +# A fully lolcat-colored box including unicode eyes. + +BOX designA + +sample +          .-"```"-. +         /_______; \ +        (_________)\| +        / / 👁 👁 \ \(_) +       / ( \___/ ) \ +   _ooo\__\_____/__/____ +  /                     \ + |     Hello World!      | +  \_________________ooo_/ +       /           \ +      /:.:.:.:.:.:.:\ +          |  |  | +          \==|==/  jgs +          /-'Y'-\ +         (__/ \__) +ends + +# Monochrome sample: +# .-"```"-. +# /_______; \ +# (_________)\| +# / / 👁 👁 \ \(_) +# / ( \___/ ) \ +# _ooo\__\_____/__/____ +# / \ +# | Hello World! | +# \_________________ooo_/ +# / \ +# /:.:.:.:.:.:.:\ +# | | | +# \==|==/ jgs +# /-'Y'-\ +# (__/ \__) + +delim ~" + +shapes { + n ("      .-~"```~"-. ", + "     /_______; \ ", + "    (_________)\| ", + "    / / 👁 👁 \ \(_) ", + "   / ( \___/ ) \ ", + "ooo\__\_____/__/___", + " ") + s ("________________ooo", + "   /           \ ", + "  /:.:.:.:.:.:.:\ ", + "      |  |  | ", + "      \==|==/  jgs ", + "      /-'Y'-\ ", + "     (__/ \__) ") + e (" |") + w ("| ") + sw (" \", " ", " ", " ", " ", " ", " ") + se ("/ ", " ", " ", " ", " ", " ", " ") + ssw ("_", " ", " ", " ", " ", " ", " ") + sse ("_", " ", " ", " ", " ", " ", " ") + nw (" ", " ", " ", " ", " ", " ", " /") + nnw (" ", " ", " ", " ", " ", "_", " ") + nne (" ", " ", " ", " ", " ", "_", " ") + ne (" ", " ", " ", " ", " ", " ", "\ ") +} + +elastic (nne,nnw, sse,ssw, e, w) + +END designA diff --git a/utest/bxstring_test.c b/utest/bxstring_test.c index 4a0fcda..1354f5f 100644 --- a/utest/bxstring_test.c +++ b/utest/bxstring_test.c @@ -353,6 +353,34 @@ void test_ansi_unicode_null(void **state) +void test_ansi_unicode_tc183(void **state) +{ + UNUSED(state); + + uint32_t *ustr32 = u32_strconv_from_arg("\x1b[38;5;43m|\x1b[39m\x1b[38;5;49m \x1b[39m X", "ASCII"); + assert_non_null(ustr32); + bxstr_t *actual = bxs_from_unicode(ustr32); + + assert_non_null(actual); + assert_non_null(actual->memory); + assert_string_equal("| X", actual->ascii); + assert_int_equal(0, (int) actual->indent); + assert_int_equal(7, (int) actual->num_columns); + assert_int_equal(37, (int) actual->num_chars); + assert_int_equal(7, (int) actual->num_chars_visible); + assert_int_equal(30, (int) actual->num_chars_invisible); + assert_int_equal(0, (int) actual->trailing); + int expected_firstchar_idx[] = {0, 16, 32, 33, 34, 35, 36, 37}; + assert_array_equal(expected_firstchar_idx, actual->first_char, 8); + int expected_vischar_idx[] = {10, 26, 32, 33, 34, 35, 36, 37}; + assert_array_equal(expected_vischar_idx, actual->visible_char, 8); + + BFREE(ustr32); + bxs_free(actual); +} + + + void test_bxs_new_empty_string(void **state) { UNUSED(state); diff --git a/utest/bxstring_test.h b/utest/bxstring_test.h index 8ee596b..0130e00 100644 --- a/utest/bxstring_test.h +++ b/utest/bxstring_test.h @@ -35,6 +35,7 @@ void test_ansi_unicode_illegalchar(void **state); void test_ansi_unicode_tabs(void **state); void test_ansi_unicode_broken_escapes(void **state); void test_ansi_unicode_null(void **state); +void test_ansi_unicode_tc183(void **state); void test_bxs_new_empty_string(void **state); diff --git a/utest/main.c b/utest/main.c index 779623b..af070cd 100644 --- a/utest/main.c +++ b/utest/main.c @@ -135,6 +135,7 @@ int main(void) cmocka_unit_test_setup(test_ansi_unicode_tabs, beforeTest), cmocka_unit_test_setup(test_ansi_unicode_broken_escapes, beforeTest), cmocka_unit_test_setup(test_ansi_unicode_null, beforeTest), + cmocka_unit_test_setup(test_ansi_unicode_tc183, beforeTest), cmocka_unit_test_setup(test_bxs_new_empty_string, beforeTest), cmocka_unit_test_setup(test_bxs_is_blank, beforeTest), cmocka_unit_test_setup(test_bxs_strdup, beforeTest),