diff --git a/src/boxes.c b/src/boxes.c index 999f6ac..4a64423 100644 --- a/src/boxes.c +++ b/src/boxes.c @@ -108,6 +108,7 @@ static int build_design(design_t **adesigns, const char *cld) bxstr_t *cldW = bxs_from_unicode(cld_u32); BFREE(cld_u32); + dp->shape[W].name = W; dp->shape[W].height = 1; dp->shape[W].width = cldW->num_columns; dp->shape[W].elastic = 1; @@ -154,6 +155,7 @@ static int build_design(design_t **adesigns, const char *cld) fprintf(stderr, "%s: internal error\n", PROJECT); return 1; /* never happens ;-) */ } + c->name = i; rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs)); if (rc) { diff --git a/src/boxes.in.h b/src/boxes.in.h index 36add79..ca27bc3 100644 --- a/src/boxes.in.h +++ b/src/boxes.in.h @@ -20,7 +20,7 @@ #ifndef BOXES_H #define BOXES_H -/* #define DEBUG 1 */ +#define DEBUG 1 /* #define REGEXP_DEBUG 1 */ /* #define PARSER_DEBUG 1 */ /* #define LEXER_DEBUG 1 */ diff --git a/src/detect.c b/src/detect.c index df198a9..ad4d87d 100644 --- a/src/detect.c +++ b/src/detect.c @@ -383,20 +383,21 @@ static size_t find_horizontal_shape(design_t *current_design, comparison_t comp_ continue; } - uint32_t *shape_relevant = prepare_comp_shape(current_design, hshape, j, comp_type, 0, 0); + uint32_t *shape_relevant = prepare_comp_shape(current_design, hshape, j, comp_type, + is_blank_leftward(current_design, hshape, j), + is_blank_rightward(current_design, hshape, j)); size_t length_relevant = u32_strlen(shape_relevant); for (size_t k = 0; k < current_design->shape[hshape].height; ++k) { - size_t a = k; + size_t line_idx = k; if (hshape >= SSE && hshape <= SSW) { - a += input.num_lines - current_design->shape[hshape].height; + line_idx += input.num_lines - current_design->shape[hshape].height; } - if (a >= input.num_lines) { + if (line_idx >= input.num_lines) { break; } - uint32_t *input_relevant = prepare_comp_input(a, 1, comp_type, 0, NULL, NULL); - /* CHECK this eats blank NW corners */ + uint32_t *input_relevant = prepare_comp_input(line_idx, 1, comp_type, 0, NULL, NULL); uint32_t *p = u32_strstr(input_relevant, shape_relevant); if (p) { if (current_design->shape[hshape].elastic) { diff --git a/src/parsecode.c b/src/parsecode.c index 96f000f..3b2497c 100644 --- a/src/parsecode.c +++ b/src/parsecode.c @@ -602,6 +602,13 @@ int action_finalize_shapes(pass_to_bison *bison_args) (int) curdes.minwidth, (int) curdes.minheight); fprintf(stderr, " Parser: Maximum shape height: %d\n", (int) curdes.maxshapeheight); #endif + + /* + * Set name of each shape + */ + for (i = 0; i < NUM_SHAPES; ++i) { + curdes.shape[i].name = i; + } return RC_SUCCESS; } diff --git a/src/shape.c b/src/shape.c index 8b7fead..bf26c6f 100644 --- a/src/shape.c +++ b/src/shape.c @@ -24,9 +24,9 @@ #include #include +#include "boxes.h" #include "bxstring.h" #include "shape.h" -#include "boxes.h" #include "tools.h" @@ -333,4 +333,226 @@ int empty_side(sentry_t *sarr, const int aside) +static int is_west(sentry_t *shape, int include_corners) +{ + size_t offset = include_corners ? 0 : 1; + for (size_t i = offset; i < SHAPES_PER_SIDE - offset; i++) { + if (west_side[i] == shape->name) { + return 1; + } + } + return 0; +} + + + +static int is_east(sentry_t *shape, int include_corners) +{ + size_t offset = include_corners ? 0 : 1; + for (size_t i = offset; i < SHAPES_PER_SIDE - offset; i++) { + if (east_side[i] == shape->name) { + return 1; + } + } + return 0; +} + + + +static int find_in_horiz(shape_t side[], shape_t shape_name) +{ + int result = -1; + for (size_t i = 0; i < SHAPES_PER_SIDE; i++) { + if (side[i] == shape_name) { + result = i; + break; + } + } + return result; +} + + + +static int find_in_north(shape_t shape_name) +{ + return find_in_horiz(north_side, shape_name); +} + + + +static int find_in_south(shape_t shape_name) +{ + return find_in_horiz(south_side_rev, shape_name); +} + + + +static int *new_blankward_cache(const size_t shape_height) +{ + int *result = (int *) calloc(shape_height, sizeof(int)); + for (size_t i = 0; i < shape_height; i++) { + result[i] = -1; + } + return result; +} + + + +int is_blank_leftward(design_t *current_design, const shape_t shape, const size_t shape_line_idx) +{ + if (current_design == NULL) { + return 0; /* this would be a bug */ + } + sentry_t *shape_data = current_design->shape + shape; + if (shape_line_idx >= shape_data->height) { + return 0; /* this would be a bug */ + } + if (shape_data->blank_leftward != NULL && shape_data->blank_leftward[shape_line_idx] >= 0) { + return shape_data->blank_leftward[shape_line_idx]; /* cached value available */ + } + if (shape_data->blank_leftward == NULL) { + shape_data->blank_leftward = new_blankward_cache(shape_data->height); + } + + int result = -1; + if (is_west(shape_data, 1)) { + result = 1; + } + else if (is_east(shape_data, 0)) { + result = 0; + } + else { + shape_t *side = north_side; + int pos = find_in_north(shape); + if (pos < 0) { + side = south_side_rev; + pos = find_in_south(shape); + } + result = 1; + for (size_t i = 0; i < (size_t) pos; i++) { + sentry_t *tshape = current_design->shape + side[i]; + if (tshape->mbcs != NULL && !bxs_is_blank(tshape->mbcs[shape_line_idx])) { + result = 0; + break; + } + } + } + + shape_data->blank_leftward[shape_line_idx] = result; + return result; +} + + +// TODO HERE is_blank_rightward() and is_blank_leftward() are nearly identical -> consolidate +int is_blank_rightward(design_t *current_design, const shape_t shape, const size_t shape_line_idx) +{ + if (current_design == NULL) { + return 0; /* this would be a bug */ + } + sentry_t *shape_data = current_design->shape + shape; + if (shape_line_idx >= shape_data->height) { + return 0; /* this would be a bug */ + } + if (shape_data->blank_rightward != NULL && shape_data->blank_rightward[shape_line_idx] >= 0) { + return shape_data->blank_rightward[shape_line_idx]; /* cached value available */ + } + if (shape_data->blank_rightward == NULL) { + shape_data->blank_rightward = new_blankward_cache(shape_data->height); + } + + int result = -1; + if (is_west(shape_data, 0)) { + result = 0; + } + else if (is_east(shape_data, 1)) { + result = 1; + } + else { + shape_t *side = north_side; + int pos = find_in_north(shape); + if (pos < 0) { + side = south_side_rev; + pos = find_in_south(shape); + } + result = 1; + for (size_t i = (size_t) pos + 1; i < SHAPES_PER_SIDE; i++) { + sentry_t *tshape = current_design->shape + side[i]; + if (tshape->mbcs != NULL && !bxs_is_blank(tshape->mbcs[shape_line_idx])) { + result = 0; + break; + } + } + } + + shape_data->blank_rightward[shape_line_idx] = result; + return result; +} + + + +void debug_print_shape(sentry_t *shape) +{ + #ifdef DEBUG + if (shape == NULL) { + fprintf(stderr, "NULL\n"); + return; + } + fprintf(stderr, "Shape %3s (%dx%d): elastic=%s, bl=", + shape_name[shape->name], (int) shape->width, (int) shape->height, shape->elastic ? "true" : "false"); + if (shape->blank_leftward == NULL) { + fprintf(stderr, "NULL"); + } + else { + fprintf(stderr, "["); + for (size_t i = 0; i < shape->height; i++) { + fprintf(stderr, "%d%s", shape->blank_leftward[i], + shape->height > 0 && i < (shape->height - 1) ? ", " : ""); + } + fprintf(stderr, "]"); + } + fprintf(stderr, ", br="); + if (shape->blank_rightward == NULL) { + fprintf(stderr, "NULL"); + } + else { + fprintf(stderr, "["); + for (size_t i = 0; i < shape->height; i++) { + fprintf(stderr, "%d%s", shape->blank_rightward[i], + shape->height > 0 && i < (shape->height - 1) ? ", " : ""); + } + fprintf(stderr, "]"); + } + fprintf(stderr, ", ascii="); + if (shape->chars == NULL) { + fprintf(stderr, "NULL"); + } + else { + fprintf(stderr, "["); + for (size_t i = 0; i < shape->height; i++) { + fprintf(stderr, "%s%s%s%s", shape->chars[i] != NULL ? "\"" : "", shape->chars[i], + shape->chars[i] != NULL ? "\"" : "", (int) i < ((int) shape->height) - 1 ? ", " : ""); + } + fprintf(stderr, "]"); + } + fprintf(stderr, ", mbcs="); + if (shape->mbcs == NULL) { + fprintf(stderr, "NULL"); + } + else { + fprintf(stderr, "["); + for (size_t i = 0; i < shape->height; i++) { + char *out_mbcs = bxs_to_output(shape->mbcs[i]); + fprintf(stderr, "%s%s%s%s", shape->mbcs[i] != NULL ? "\"" : "", out_mbcs, + shape->mbcs[i] != NULL ? "\"" : "", shape->height > 0 && i < (shape->height - 1) ? ", " : ""); + BFREE(out_mbcs); + } + fprintf(stderr, "]"); + } + fprintf(stderr, "\n"); + #else + UNUSED(shape); + #endif +} + + /* vim: set sw=4: */ diff --git a/src/shape.h b/src/shape.h index a70da60..a40c4cf 100644 --- a/src/shape.h +++ b/src/shape.h @@ -46,14 +46,25 @@ extern shape_t *sides[NUM_SIDES]; typedef struct { + shape_t name; char **chars; bxstr_t **mbcs; size_t height; size_t width; - int elastic; /* elastic is used only in original definition */ + + /** elastic is used only in original definition */ + int elastic; + + /** For each shape line 0..height-1, a flag which is 1 if all shapes to the left of this shape are blank on the + * same shape line. Always 1 if the shape is part of the left (west) box side. */ + int *blank_leftward; + + /** For each shape line 0..height-1, a flag which is 1 if all shapes to the right of this shape are blank on the + * same shape line. Always 1 if the shape is part of the right (east) box side. */ + int *blank_rightward; } sentry_t; -#define SENTRY_INITIALIZER (sentry_t) {NULL, NULL, 0, 0, 0} +#define SENTRY_INITIALIZER (sentry_t) {NW, NULL, NULL, 0, 0, 0, NULL, NULL} @@ -72,6 +83,34 @@ size_t widest (const sentry_t *sarr, const int n, ...); int empty_side (sentry_t *sarr, const int aside); +/** + * Determine if there are only blanks to the left of this shape on the given line. The result is cached in the shape. + * @param current_design the design whose shapes to use + * @param shape the shape for which to calculate "blank_leftward" + * @param shape_line_idx the index of the shape line to assess + * @return 1 if blank leftward, 0 otherwise. Will always return 1 if shape is part of the left (west) box side, and + * always 0 if shape is an east side shape (not a corner) + */ +int is_blank_leftward(design_t *current_design, const shape_t shape, const size_t shape_line_idx); + +/** + * Determine if there are only blanks to the right of this shape on the given line. The result is cached in the shape. + * @param current_design the design whose shapes to use + * @param shape the shape for which to calculate "blank_rightward" + * @param shape_line_idx the index of the shape line to assess + * @return 1 if blank rightward, 0 otherwise. Will always return 1 if shape is part of the right (east) box side, and + * always 0 if shape is a west side shape (not a corner) + */ +int is_blank_rightward(design_t *current_design, const shape_t shape, const size_t shape_line_idx); + + +/** + * Print complete data about a shape to stderr for debugging. + * @param shape the shape whose data to print + */ +void debug_print_shape(sentry_t *shape); + + #endif /*SHAPE_H*/ diff --git a/test/190_remove_blank_nw_lines.txt b/test/190_remove_blank_nw_lines.txt new file mode 100644 index 0000000..0ffb98a --- /dev/null +++ b/test/190_remove_blank_nw_lines.txt @@ -0,0 +1,27 @@ +:DESC +Auto-detect the box design as 'capgirl', remove the box, and also remove the box indentation because `-i none`. +The resulting text will still appear a bit indented, but those are the spaces from inside the box! +Note that the NW corner is mostly blank (except its very bottom), which triggers some potential problems in the code. + +:ARGS +--remove -i none +:INPUT + .-"```"-. + /_______; \ + (_________)\| + / / a a \ \(_) + / ( \___/ ) \ + _ooo\__\_____/__/____ + / \ + | foobar | + \_________________ooo_/ + / \ + /:.:.:.:.:.:.:\ + | | | + \==|==/ jgs + /-'Y'-\ + (__/ \__) +:OUTPUT-FILTER +:EXPECTED + foobar +:EOF