• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ascii-boxes / boxes / 6683464098

29 Oct 2023 12:48PM UTC coverage: 83.972%. Remained the same
6683464098

push

github

tsjensen
Test and fix handling of line comment removal

2421 of 3166 branches covered (0.0%)

Branch coverage included in aggregate %.

48 of 48 new or added lines in 2 files covered. (100.0%)

3803 of 4246 relevant lines covered (89.57%)

11500.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.9
/src/remove.c
1
/*
2
 * boxes - Command line filter to draw/remove ASCII boxes around text
3
 * Copyright (c) 1999-2023 Thomas Jensen and the boxes contributors
4
 *
5
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
6
 * License, version 3, as published by the Free Software Foundation.
7
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
8
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
9
 * details.
10
 * You should have received a copy of the GNU General Public License along with this program.
11
 * If not, see <https://www.gnu.org/licenses/>.
12
 *
13
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14
 */
15

16
/*
17
 * Box removal, i.e. the deletion of boxes
18
 */
19

20
#include "config.h"
21

22
#include <stdint.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <unistr.h>
27
#include <uniwidth.h>
28

29
#include "boxes.h"
30
#include "detect.h"
31
#include "remove.h"
32
#include "shape.h"
33
#include "tools.h"
34
#include "unicode.h"
35

36

37

38
typedef struct _line_ctx_t {
39
    /** index of the first character of the west shape */
40
    size_t west_start;
41

42
    /** index of the character following the last character of the west shape. If equal to `west_start`, then no west
43
     *  shape was detected. */
44
    size_t west_end;
45

46
    /** the length in characters of the matched west shape part */
47
    size_t west_quality;
48

49
    /** index of the first character of the east shape */
50
    size_t east_start;
51

52
    /** index of the character following the last character of the east shape. If equal to `east_start`, then no east
53
     *  shape was detected.  */
54
    size_t east_end;
55

56
    /** the length in characters of the matched east shape part */
57
    size_t east_quality;
58

59
    /** the input line to which the above values refer. Will look very different depending on comparison type. */
60
    uint32_t *input_line_used;
61
} line_ctx_t;
62

63

64

65
typedef struct _remove_ctx_t {
66
    /** Array of flags indicating which sides of the box design are defined as empty. Access via `BTOP` etc. constants. */
67
    int empty_side[NUM_SIDES];
68

69
    /** Flag indicating that there are no invisible characters in the definition of the design we are removing. */
70
    int design_is_mono;
71

72
    /** Flag indicating that there are no invisible characters in the input. */
73
    int input_is_mono;
74

75
    /** Index into `input.lines` of the first line of the box (topmost box line). Lines above are blank. */
76
    size_t top_start_idx;
77

78
    /** Index into `input.lines` of the line following the last line of the top part of the box. If the top part of the
79
     *  box is empty or missing, this value will be equal to `top_start_idx`. */
80
    size_t top_end_idx;
81

82
    /** Index into `input.lines` of the first line of the bottom side of the box. */
83
    size_t bottom_start_idx;
84

85
    /** Index into `input.lines` of the line following the last line of the bottom part of the box. If the bottom part
86
     *  of the box is empty or missing, this value will be equal to `bottom_start_idx`. Lines below are blank. */
87
    size_t bottom_end_idx;
88

89
    /** The current comparison type. This changes whenever another comparison type is tried. */
90
    comparison_t comp_type;
91

92
    /** number of lines in `body` */
93
    size_t body_num_lines;
94

95
    /** Information on the vertical east and west shapes in body lines, one entry for each line between `top_end_idx`
96
     *  (inclusive) and `bottom_start_idx` (exclusive) */
97
    line_ctx_t *body;
98
} remove_ctx_t;
99

100

101

102
static void debug_print_remove_ctx(remove_ctx_t *ctx, char *heading)
14✔
103
{
104
    #ifdef DEBUG
105
        fprintf(stderr, "Remove Context %s:\n", heading);
106
        fprintf(stderr, "    - empty_side[BTOP] = %s\n", ctx->empty_side[BTOP] ? "true" : "false");
107
        fprintf(stderr, "    - empty_side[BRIG] = %s\n", ctx->empty_side[BRIG] ? "true" : "false");
108
        fprintf(stderr, "    - empty_side[BBOT] = %s\n", ctx->empty_side[BBOT] ? "true" : "false");
109
        fprintf(stderr, "    - empty_side[BLEF] = %s\n", ctx->empty_side[BLEF] ? "true" : "false");
110
        fprintf(stderr, "    - design_is_mono = %s\n", ctx->design_is_mono ? "true" : "false");
111
        fprintf(stderr, "    - input_is_mono = %s\n", ctx->input_is_mono ? "true" : "false");
112
        fprintf(stderr, "    - top_start_idx = %d\n", (int) ctx->top_start_idx);
113
        fprintf(stderr, "    - top_end_idx = %d\n", (int) ctx->top_end_idx);
114
        fprintf(stderr, "    - bottom_start_idx = %d\n", (int) ctx->bottom_start_idx);
115
        fprintf(stderr, "    - bottom_end_idx = %d\n", (int) ctx->bottom_end_idx);
116
        fprintf(stderr, "    - comp_type = %s\n", comparison_name[ctx->comp_type]);
117
        fprintf(stderr, "    - body (%d lines):\n", (int) ctx->body_num_lines);
118
        for (size_t i = 0; i < ctx->body_num_lines; i++) {
119
            if (ctx->body[i].input_line_used != NULL) {
120
                char *out_input_line_used = u32_strconv_to_output(ctx->body[i].input_line_used);
121
                fprintf(stderr, "        - lctx: \"%s\" (%d characters)\n", out_input_line_used,
122
                    (int) u32_strlen(ctx->body[i].input_line_used));
123
                BFREE(out_input_line_used);
124
            }
125
            else {
126
                fprintf(stderr, "        - lctx: (null)\n");
127
            }
128
            bxstr_t *orgline = input.lines[ctx->top_end_idx + i].text;
129
            if (orgline != NULL) {
130
                char *out_orgline = bxs_to_output(orgline);
131
                fprintf(stderr, "          orgl: \"%s\" (%d characters, %d columns)\n", out_orgline,
132
                    (int) orgline->num_chars, (int) orgline->num_columns);
133
                BFREE(out_orgline);
134
            }
135
            else {
136
                fprintf(stderr, "          orgl: (null)\n");
137
            }
138
            fprintf(stderr, "                west: %d-%d (quality: %d), east: %d-%d (quality: %d)\n",
139
                (int) ctx->body[i].west_start, (int) ctx->body[i].west_end, (int) ctx->body[i].west_quality,
140
                (int) ctx->body[i].east_start, (int) ctx->body[i].east_end, (int) ctx->body[i].east_quality);
141
        }
142
    #else
143
        UNUSED(ctx);
144
        UNUSED(heading);
145
    #endif
146
}
14✔
147

148

149

150
static void debug_print_shapes_relevant(shape_line_ctx_t *shapes_relevant)
95✔
151
{
152
    #ifdef DEBUG
153
        fprintf(stderr, "  shapes_relevant = {");
154
        for (size_t ds = 0; ds < SHAPES_PER_SIDE; ds++) {
155
            if (shapes_relevant[ds].empty) {
156
                fprintf(stderr, "-");
157
            }
158
            else {
159
                char *out_shp_text = bxs_to_output(shapes_relevant[ds].text);
160
                fprintf(stderr, "\"%s\"(%d%s)", out_shp_text, (int) shapes_relevant[ds].text->num_chars,
161
                    shapes_relevant[ds].elastic ? "E" : "");
162
                BFREE(out_shp_text);
163
            }
164
            if (ds < SHAPES_PER_SIDE - 1) {
165
                fprintf(stderr, ", ");
166
            }
167
        }
168
        fprintf(stderr, "}\n");
169
    #else
170
        UNUSED(shapes_relevant);
171
    #endif
172
}
95✔
173

174

175

176
static size_t find_first_line()
14✔
177
{
178
    size_t result = input.num_lines;
14✔
179
    for (size_t line_idx = 0; line_idx < input.num_lines; line_idx++) {
14!
180
        if (!bxs_is_blank(input.lines[line_idx].text)) {
14!
181
            result = line_idx;
14✔
182
            break;
14✔
183
        }
184
    }
185
    return result;
14✔
186
}
187

188

189

190
static size_t find_last_line()
14✔
191
{
192
    size_t result = input.num_lines - 1;
14✔
193
    for (long line_idx = (long) input.num_lines - 1; line_idx >= 0; line_idx--) {
14!
194
        if (!bxs_is_blank(input.lines[line_idx].text)) {
14!
195
            result = (size_t) line_idx;
14✔
196
            break;
14✔
197
        }
198
    }
199
    return result;
14✔
200
}
201

202

203

204
static int is_shape_line_empty(shape_line_ctx_t *shapes_relevant, size_t shape_idx)
232✔
205
{
206
    if (shape_idx < SHAPES_PER_SIDE) {
232!
207
        return shapes_relevant[shape_idx].empty || bxs_is_blank(shapes_relevant[shape_idx].text);
232✔
208
    }
209
    return 1;
×
210
}
211

212

213

214
static int non_empty_shapes_after(shape_line_ctx_t *shapes_relevant, size_t shape_idx)
141✔
215
{
216
    for (size_t i = shape_idx + 1; i < SHAPES_PER_SIDE - 1; i++) {
214✔
217
        if (!is_shape_line_empty(shapes_relevant, i)) {
80✔
218
            return 1;
7✔
219
        }
220
    }
221
    return 0;
134✔
222
}
223

224

225

226
static int is_blank_between(uint32_t *start, uint32_t *end)
43✔
227
{
228
    for (uint32_t *p = start; p < end; p++) {
170✔
229
        if (!is_blank(*p)) {
128✔
230
            return 0;
1✔
231
        }
232
    }
233
    return 1;
42✔
234
}
235

236

237

238
/**
239
 * Take a shape line and shorten it by cutting off blanks from both ends.
240
 * @param shape_line_ctx info record on the shape line to work on. Contains the original shape line, unshortened.
241
 * @param quality (IN/OUT) the current quality, here the value that was last tested. We will reduce this by one.
242
 * @param prefer_left if 1, first cut all blanks from the start of the shape line, if 0, first cut at the end
243
 * @param allow_left if 1, blanks may be cut from the left of the shape line, if 0, we never cut from the left
244
 * @param allow_right if 1, blanks may be cut from the right of the shape line, if 0, we never cut from the right
245
 * @return the shortened shape line, in new memory, or NULL if further shortening was not possible
246
 */
247
uint32_t *shorten(shape_line_ctx_t *shape_line_ctx, size_t *quality, int prefer_left, int allow_left, int allow_right)
670✔
248
{
249
    if (shape_line_ctx == NULL || shape_line_ctx->text == NULL || quality == NULL
670✔
250
            || *quality > shape_line_ctx->text->num_chars) {
667✔
251
        return NULL;
4✔
252
    }
253

254
    uint32_t *s = shape_line_ctx->text->memory;
666✔
255
    uint32_t *e = shape_line_ctx->text->memory + shape_line_ctx->text->num_chars;
666✔
256
    prefer_left = allow_left ? prefer_left : 0;
666✔
257
    size_t reduction_steps = shape_line_ctx->text->num_chars - *quality + 1;
666✔
258
    for (size_t i = 0; i < reduction_steps; i++) {
1,824✔
259
        if (prefer_left) {
1,410✔
260
            if (s < e && is_blank(*s)) {
450!
261
                s++;
110✔
262
            }
263
            else if (e > s && allow_right && is_blank(*(e - 1))) {
340!
264
                e--;
227✔
265
            }
266
            else {
267
                break;
268
            }
269
        }
270
        else {
271
            if (e > s && allow_right && is_blank(*(e - 1))) {
960!
272
                e--;
574✔
273
            }
274
            else if (s < e && allow_left && is_blank(*s)) {
386!
275
                s++;
247✔
276
            }
277
            else {
278
                break;
279
            }
280
        }
281
    }
282

283
    uint32_t *result = NULL;
666✔
284
    size_t new_quality = e - s;
666✔
285
    if (new_quality < *quality) {
666✔
286
        result = u32_strdup(s);
414✔
287
        set_char_at(result, new_quality, char_nul);
414✔
288
        *quality = new_quality;
414✔
289
    }
290
    return result;
666✔
291
}
292

293

294

295
static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos,
296
        int anchored_right);
297

298

299

300
/**
301
 * (horizontal middle match)
302
 * Recursive helper function for match_horiz_line(), uses backtracking.
303
 * @param shapes_relevant the prepared shape lines to be concatenated
304
 * @param cur_pos current position in the input line being matched
305
 * @param shape_idx index into `shapes_relevant` indicating which shape to try now
306
 * @param end_pos first character of the east corner
307
 * @param anchored_left flag indicating that `cur_pos` is already "anchored" or still "shiftable". "Anchored" means
308
 *      that we have matched a non-blank shape line already (corner shape line was not blank). Else "shiftable".
309
 * @param anchored_right flag indicating that the east corner shape was not blank. If this is `false`, it means that
310
 *      a shape may be shortened right if only blank shape lines follow.
311
 * @return `== 1`: success;
312
 *         `== 0`: failed to match
313
 */
314
int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos, int anchored_left,
1,079✔
315
        int anchored_right)
316
{
317
    #ifdef DEBUG
318
        char *out_cur_pos = u32_strconv_to_output(cur_pos);
319
        char *out_end_pos = u32_strconv_to_output(end_pos);
320
        fprintf(stderr, "hmm(shapes_relevant, \"%s\", %d, \"%s\", %s, %s) - enter\n", out_cur_pos,
321
                (int) shape_idx, out_end_pos, anchored_left ? "true" : "false", anchored_right ? "true" : "false");
322
        BFREE(out_cur_pos);
323
        BFREE(out_end_pos);
324
    #endif
325

326
    int result = 0;
1,079✔
327
    if (!anchored_left) {
1,079✔
328
        result = hmm_shiftable(shapes_relevant, cur_pos, shape_idx, end_pos, anchored_right);
55✔
329
    }
330
    else if (cur_pos > end_pos) {
1,024✔
331
        /* invalid input */
332
        result = 0;
13✔
333
    }
334
    else if (cur_pos == end_pos) {
1,011✔
335
        /* we are at the end, which is fine if there is nothing else to match */
336
        result = (shapes_relevant[shape_idx].empty || bxs_is_blank(shapes_relevant[shape_idx].text))
90✔
337
                && !non_empty_shapes_after(shapes_relevant, shape_idx) ? 1 : 0;
90!
338
    }
339
    else if (shape_idx >= SHAPES_PER_SIDE - 1) {
965✔
340
        /* no more shapes to try, which is fine if the rest of the line is blank */
341
        result = u32_is_blank(cur_pos);
1✔
342
    }
343
    else if (shapes_relevant[shape_idx].empty) {
964✔
344
        /* the current shape line is empty, try the next one */
345
        result = hmm(shapes_relevant, cur_pos, shape_idx + 1, end_pos, 1, anchored_right);
15✔
346
    }
347
    else {
348
        uint32_t *shape_line = u32_strdup(shapes_relevant[shape_idx].text->memory);
949✔
349
        size_t quality = shapes_relevant[shape_idx].text->num_chars;
949✔
350
        while (shape_line != NULL) {
1,910✔
351
            if (u32_strncmp(cur_pos, shape_line, quality) == 0) {
961✔
352
                BFREE(shape_line);
835!
353
                cur_pos = cur_pos + quality;
835✔
354
                if (cur_pos == end_pos && !non_empty_shapes_after(shapes_relevant, shape_idx)) {
835✔
355
                    result = 1; /* success */
52✔
356
                }
357
                else {
358
                    int rc = 0;
783✔
359
                    if (shapes_relevant[shape_idx].elastic) {
783✔
360
                        rc = hmm(shapes_relevant, cur_pos, shape_idx, end_pos, 1, anchored_right);
748✔
361
                    }
362
                    if (rc == 0) {
783✔
363
                        result = hmm(shapes_relevant, cur_pos, shape_idx + 1, end_pos, 1, anchored_right);
168✔
364
                    }
365
                    else {
366
                        result = rc;
615✔
367
                    }
368
                }
369
            }
370
            else if (!anchored_right) {
126✔
371
                shape_line = shorten(shapes_relevant + shape_idx, &quality, 0, 0, 1);
17✔
372
            }
373
            else {
374
                BFREE(shape_line);
109!
375
            }
376
        }
377
    }
378

379
    #ifdef DEBUG
380
        fprintf(stderr, "hmm() - exit, result = %d\n", result);
381
    #endif
382
    return result;
1,079✔
383
}
384

385

386

387
static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos,
55✔
388
        int anchored_right)
389
{
390
    int result = 0;
55✔
391
    int shapes_are_empty = 1;
55✔
392
    for (size_t i = shape_idx; i < SHAPES_PER_SIDE - 1; i++) {
106✔
393
        if (!is_shape_line_empty(shapes_relevant, i)) {
105✔
394
            shapes_are_empty = 0;
54✔
395
            int can_shorten_right = -1;
54✔
396
            size_t quality = shapes_relevant[i].text->num_chars;
54✔
397
            uint32_t *shape_line = shapes_relevant[i].text->memory;
54✔
398
            while (shape_line != NULL) {
219✔
399
                uint32_t *p = u32_strstr(cur_pos, shape_line);
206✔
400
                if (p != NULL && p < end_pos && is_blank_between(cur_pos, p)) {
206!
401
                    result = hmm(shapes_relevant, p + quality, i + (shapes_relevant[i].elastic ? 0 : 1),
41✔
402
                            end_pos, 1, anchored_right);
403
                    if (result == 0 && shapes_relevant[i].elastic) {
41!
404
                        result = hmm(shapes_relevant, p + quality, i + 1, end_pos, 1, anchored_right);
3✔
405
                    }
406
                    break;
41✔
407
                }
408
                if (can_shorten_right == -1) {
165✔
409
                    /* we can only shorten right if the east corner shape line is also empty */
410
                    can_shorten_right = non_empty_shapes_after(shapes_relevant, i)
96✔
411
                            || !is_shape_line_empty(shapes_relevant, SHAPES_PER_SIDE - 1) ? 0 : 1;
48✔
412
                }
413
                shape_line = shorten(shapes_relevant + i, &quality, 0, 1, can_shorten_right);
165✔
414
            }
415
            break;
54✔
416
        }
417
    }
418
    if (shapes_are_empty) {
55✔
419
        /* all shapes were empty, which is fine if line was blank */
420
        result = is_blank_between(cur_pos, end_pos);
1✔
421
    }
422
    return result;
55✔
423
}
424

425

426

427
static shape_line_ctx_t *prepare_comp_shapes_horiz(int hside, comparison_t comp_type, size_t shape_line_idx)
95✔
428
{
429
    shape_t *side_shapes = hside == BTOP ? north_side : south_side_rev;
95✔
430
    shape_line_ctx_t *shapes_relevant = (shape_line_ctx_t *) calloc(SHAPES_PER_SIDE, sizeof(shape_line_ctx_t));
95✔
431

432
    for (size_t i = 0; i < SHAPES_PER_SIDE; i++) {
570✔
433
        shapes_relevant[i].elastic = opt.design->shape[side_shapes[i]].elastic;
475✔
434
        shapes_relevant[i].empty = isempty(opt.design->shape + side_shapes[i]);
475✔
435
        if (!shapes_relevant[i].empty) {
475✔
436
            uint32_t *s = prepare_comp_shape(opt.design, side_shapes[i], shape_line_idx, comp_type, 0,
447✔
437
                    i == SHAPES_PER_SIDE - 1);
438
            shapes_relevant[i].text = bxs_from_unicode(s);
447✔
439
            BFREE(s);
447!
440
        }
441
    }
442

443
    return shapes_relevant;
95✔
444
}
445

446

447

448
static match_result_t *new_match_result(uint32_t *p, size_t p_idx, size_t len, int shiftable)
194✔
449
{
450
    match_result_t *result = (match_result_t *) calloc(1, sizeof(match_result_t));
194✔
451
    result->p = p;
194✔
452
    result->p_idx = p_idx;
194✔
453
    result->len = len;
194✔
454
    result->shiftable = shiftable;
194✔
455
    return result;
194✔
456
}
457

458

459

460
/**
461
 * Match a `shape_line` at the beginning (`vside` == `BLEF`) or the end (`vside` == `BRIG`) of an `input_line`.
462
 * Both `input_line` and `shape_line` may contain invisible characters, who are then matched, too, just like any other
463
 * characters.
464
 * @param vside BLEF or BRIG
465
 * @param input_line the input line to examine. We expect that it was NOT trimmed.
466
 * @param shape_line the shape line to match, also NOT trimmed
467
 * @return pointer to the match result (in existing memory of `input_line->memory`), or `NULL` if no match
468
 */
469
match_result_t *match_outer_shape(int vside, bxstr_t *input_line, bxstr_t *shape_line)
205✔
470
{
471
    if (input_line == NULL || input_line->num_chars == 0 || shape_line == NULL || shape_line->num_chars == 0) {
205!
472
        return NULL;
2✔
473
    }
474

475
    if (vside == BLEF) {
203✔
476
        if (bxs_is_blank(shape_line)) {
101✔
477
            return new_match_result(input_line->memory, 0, 0, 1);
51✔
478
        }
479
        for (uint32_t *s = shape_line->memory; s == shape_line->memory || is_blank(*s); s++) {
57✔
480
            uint32_t *p = u32_strstr(input_line->memory, s);
53✔
481
            size_t p_idx = p != NULL ? p - input_line->memory : 0;
53✔
482
            if (p == NULL || p_idx > input_line->first_char[input_line->indent]) {
53✔
483
                continue;  /* not found or found too far in */
7✔
484
            }
485
            return new_match_result(p, p_idx, shape_line->num_chars - (s - shape_line->memory), 0);
46✔
486
        }
487
    }
488
    else {
489
        if (bxs_is_blank(shape_line)) {
102✔
490
            uint32_t *p = bxs_last_char_ptr(input_line);
51✔
491
            size_t p_idx = p - input_line->memory;
51✔
492
            return new_match_result(p, p_idx, 0, 1);
51✔
493
        }
494
        int slen = shape_line->num_chars;
51✔
495
        uint32_t *s = u32_strdup(shape_line->memory);
51✔
496
        for (; slen == (int) shape_line->num_chars || is_blank(s[slen]); slen--) {
61✔
497
            s[slen] = char_nul;
56✔
498
            uint32_t *p = u32_strnrstr(input_line->memory, s, slen);
56✔
499
            size_t p_idx = p != NULL ? p - input_line->memory : 0;
56✔
500
            if (p == NULL || p_idx + slen
56✔
501
                    < input_line->first_char[input_line->num_chars_visible - input_line->trailing]) {
48✔
502
                continue; /* not found or found too far in */
10✔
503
            }
504
            BFREE(s);
46!
505
            return new_match_result(p, p_idx, (size_t) slen, 0);
46✔
506
        }
507
        BFREE(s);
5!
508
    }
509
    return NULL;
9✔
510
}
511

512

513

514
static int match_horiz_line(remove_ctx_t *ctx, int hside, size_t input_line_idx, size_t shape_line_idx)
80✔
515
{
516
    #ifdef DEBUG
517
        fprintf(stderr, "match_horiz_line(ctx, %s, %d, %d)\n",
518
                hside == BTOP ? "BTOP" : "BBOT", (int) input_line_idx, (int) shape_line_idx);
519
    #endif
520

521
    int result = 0;
80✔
522
    for (comparison_t comp_type = 0; comp_type < NUM_COMPARISON_TYPES; comp_type++) {
179!
523
        if (!comp_type_is_viable(comp_type, ctx->input_is_mono, ctx->design_is_mono)) {
179✔
524
            continue;
84✔
525
        }
526
        ctx->comp_type = comp_type;
95✔
527
        #ifdef DEBUG
528
            fprintf(stderr, "  Setting comparison type to: %s\n", comparison_name[comp_type]);
529
        #endif
530

531
        shape_line_ctx_t *shapes_relevant = prepare_comp_shapes_horiz(hside, comp_type, shape_line_idx);
95✔
532
        debug_print_shapes_relevant(shapes_relevant);
95✔
533

534
        uint32_t *cur_pos = NULL;
95✔
535
        bxstr_t *input_prepped1 = bxs_from_unicode(prepare_comp_input(input_line_idx, 0, comp_type, 0, NULL, NULL));
95✔
536
        bxstr_t *input_prepped = bxs_rtrim(input_prepped1);
95✔
537
        bxs_free(input_prepped1);
95✔
538
        #ifdef DEBUG
539
            char *out_input_prepped = bxs_to_output(input_prepped);
540
            fprintf(stderr, "  input_prepped = \"%s\"\n", out_input_prepped);
541
            BFREE(out_input_prepped);
542
        #endif
543
        match_result_t *mrl = match_outer_shape(BLEF, input_prepped, shapes_relevant[0].text);
95✔
544
        if (mrl != NULL) {
95✔
545
            cur_pos = mrl->p + mrl->len;
93✔
546
        }
547

548
        uint32_t *end_pos = NULL;
95✔
549
        match_result_t *mrr = match_outer_shape(BRIG, input_prepped, shapes_relevant[SHAPES_PER_SIDE - 1].text);
95✔
550
        if (mrr != NULL) {
95✔
551
            end_pos = mrr->p;
92✔
552
        }
553
        #ifdef DEBUG
554
            char *out_cur_pos = u32_strconv_to_output(cur_pos);
555
            char *out_end_pos = u32_strconv_to_output(end_pos);
556
            fprintf(stderr, "  cur_pos = \"%s\" (index %d)\n", out_cur_pos, (int) BMAX(cur_pos - input_prepped->memory, 0));
557
            fprintf(stderr, "  end_pos = \"%s\" (index %d)\n", out_end_pos, (int) BMAX(end_pos - input_prepped->memory, 0));
558
            BFREE(out_cur_pos);
559
            BFREE(out_end_pos);
560
        #endif
561

562
        if (cur_pos && end_pos) {
95✔
563
            result = hmm(shapes_relevant, cur_pos, 1, end_pos, mrl->shiftable ? 0 : 1, mrr->shiftable ? 0 : 1);
92✔
564
        }
565

566
        BFREE(mrl);
95✔
567
        BFREE(mrr);
95✔
568
        for (size_t i = 0; i < SHAPES_PER_SIDE; i++) {
570✔
569
            bxs_free(shapes_relevant[i].text);
475✔
570
        }
571
        BFREE(shapes_relevant);
95!
572

573
        if (result) {
95✔
574
            #ifdef DEBUG
575
                fprintf(stderr, "Matched %s side line using comp_type=%s and shape_line_idx=%d\n",
576
                    hside == BTOP ? "top" : "bottom", comparison_name[comp_type], (int) shape_line_idx);
577
            #endif
578
            break;
80✔
579
        }
580
    }
581

582
    return result;
80✔
583
}
584

585

586

587
static size_t find_top_side(remove_ctx_t *ctx)
12✔
588
{
589
    size_t result = ctx->top_start_idx;
12✔
590
    sentry_t *shapes = opt.design->shape;
12✔
591
    for (size_t input_line_idx = ctx->top_start_idx;
12✔
592
            input_line_idx < input.num_lines && input_line_idx < ctx->top_start_idx + shapes[NE].height;
52!
593
            input_line_idx++)
40✔
594
    {
595
        int matched = 0;
40✔
596
        size_t shape_lines_tested = 0;
40✔
597
        for (size_t shape_line_idx = (input_line_idx - ctx->top_start_idx) % shapes[NE].height;
40✔
598
                shape_lines_tested < shapes[NE].height;
40!
599
                shape_line_idx = (shape_line_idx + 1) % shapes[NE].height, shape_lines_tested++)
×
600
        {
601
            if (match_horiz_line(ctx, BTOP, input_line_idx, shape_line_idx)) {
40!
602
                matched = 1;
40✔
603
                break;
40✔
604
            }
605
        }
606
        if (!matched) {
40!
607
            break;
×
608
        }
609
        result = input_line_idx + 1;
40✔
610
    }
611
    return result;
12✔
612
}
613

614

615

616
static size_t find_bottom_side(remove_ctx_t *ctx)
12✔
617
{
618
    size_t result = ctx->bottom_end_idx;
12✔
619
    sentry_t *shapes = opt.design->shape;
12✔
620
    for (long input_line_idx = (long) ctx->bottom_end_idx - 1;
12✔
621
            input_line_idx >= 0 && input_line_idx >= (long) ctx->bottom_end_idx - (long) shapes[SE].height;
52!
622
            input_line_idx--)
40✔
623
    {
624
        int matched = 0;
40✔
625
        size_t shape_lines_tested = 0;
40✔
626
        for (long shape_line_idx = shapes[SE].height - (ctx->bottom_end_idx - input_line_idx);
40✔
627
                shape_line_idx >= 0 && shape_lines_tested < shapes[SE].height;
40!
628
                shape_lines_tested++,
×
629
                shape_line_idx = shape_line_idx == 0 ? (long) (shapes[SE].height - 1) : (long) (shape_line_idx - 1))
×
630
        {
631
            if (match_horiz_line(ctx, BBOT, input_line_idx, shape_line_idx)) {
40!
632
                matched = 1;
40✔
633
                break;
40✔
634
            }
635
        }
636
        if (!matched) {
40!
637
            break;
×
638
        }
639
        result = input_line_idx;
40✔
640
    }
641
    return result;
12✔
642
}
643

644

645

646
static size_t count_shape_lines(shape_t side_shapes[])
28✔
647
{
648
    size_t result = 0;
28✔
649
    for (size_t i = 0; i < SHAPES_PER_SIDE - CORNERS_PER_SIDE; i++) {
112✔
650
        if (!isempty(opt.design->shape + side_shapes[i])) {
84✔
651
            result += opt.design->shape[side_shapes[i]].height;
36✔
652
        }
653
    }
654
    return result;
28✔
655
}
656

657

658
static shape_line_ctx_t **prepare_comp_shapes_vert(int vside, comparison_t comp_type)
28✔
659
{
660
    shape_t west_side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE] = {WNW, W, WSW};
28✔
661
    shape_t east_side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE] = {ENE, E, ESE};
28✔
662
    shape_t side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE];
663
    if (vside == BLEF) {
28✔
664
        memcpy(side_shapes, west_side_shapes, (SHAPES_PER_SIDE - CORNERS_PER_SIDE) * sizeof(shape_t));
15✔
665
    }
666
    else {
667
        memcpy(side_shapes, east_side_shapes, (SHAPES_PER_SIDE - CORNERS_PER_SIDE) * sizeof(shape_t));
13✔
668
    }
669

670
    size_t num_shape_lines = count_shape_lines(side_shapes);
28✔
671

672
    shape_line_ctx_t **shape_lines = (shape_line_ctx_t **) calloc(num_shape_lines + 1, sizeof(shape_line_ctx_t *));
28✔
673
    for (size_t i = 0; i < num_shape_lines; i++) {
90✔
674
        shape_lines[i] = (shape_line_ctx_t *) calloc(1, sizeof(shape_line_ctx_t));
62✔
675
    }
676

677
    for (size_t shape_idx = 0, i = 0; shape_idx < SHAPES_PER_SIDE - CORNERS_PER_SIDE; shape_idx++) {
112✔
678
        if (!isempty(opt.design->shape + side_shapes[shape_idx])) {
84✔
679
            int deep_empty = isdeepempty(opt.design->shape + side_shapes[shape_idx]);
36✔
680
            for (size_t slno = 0; slno < opt.design->shape[side_shapes[shape_idx]].height; slno++, i++) {
98✔
681
                uint32_t *s = prepare_comp_shape(opt.design, side_shapes[shape_idx], slno, comp_type, 0, 0);
62✔
682
                shape_lines[i]->text = bxs_from_unicode(s);
62✔
683
                shape_lines[i]->empty = deep_empty;
62✔
684
                shape_lines[i]->elastic = opt.design->shape[side_shapes[shape_idx]].elastic;
62✔
685
                BFREE(s);
62!
686
            }
687
        }
688
    }
689

690
    return shape_lines;
28✔
691
}
692

693

694

695
static void free_shape_lines(shape_line_ctx_t **shape_lines)
30✔
696
{
697
    if (shape_lines != NULL) {
30✔
698
        for (shape_line_ctx_t **p = shape_lines; *p != NULL; p++) {
90✔
699
            bxs_free((*p)->text);
62✔
700
            BFREE(*p);
62!
701
        }
702
        BFREE(shape_lines);
28!
703
    }
704
}
30✔
705

706

707

708
static void match_vertical_side(remove_ctx_t *ctx, int vside, shape_line_ctx_t **shape_lines, uint32_t *input_line,
100✔
709
    size_t line_idx, size_t input_length, size_t input_indent, size_t input_trailing)
710
{
711
    line_ctx_t *line_ctx = ctx->body + (line_idx - ctx->top_end_idx);
100✔
712

713
    for (shape_line_ctx_t **shape_line_ctx = shape_lines; *shape_line_ctx != NULL; shape_line_ctx++) {
472✔
714
        if ((*shape_line_ctx)->empty) {
372!
715
            continue;
×
716
        }
717

718
        size_t max_quality = (*shape_line_ctx)->text->num_chars;
372✔
719
        size_t quality = max_quality;
372✔
720
        uint32_t *shape_text = (*shape_line_ctx)->text->memory;
372✔
721
        uint32_t *to_free = NULL;
372✔
722
        while(shape_text != NULL) {
884✔
723
            uint32_t *p;
724
            if (vside == BLEF) {
610✔
725
                p = u32_strstr(input_line, shape_text);
307✔
726
            }
727
            else {
728
                p = u32_strnrstr(input_line, shape_text, quality);
303✔
729
            }
730
            BFREE(to_free);
610✔
731
            shape_text = NULL;
610✔
732

733
            if (p == NULL) {
610✔
734
                shape_text = shorten(*shape_line_ctx, &quality, vside == BLEF, 1, 1);
467✔
735
                to_free = shape_text;
467✔
736
            }
737
            else if (vside == BLEF && ((size_t) (p - input_line) <= input_indent + (max_quality - quality))) {
143✔
738
                if (quality > line_ctx->west_quality) {
80✔
739
                    line_ctx->west_start = (size_t) (p - input_line);
62✔
740
                    line_ctx->west_end = line_ctx->west_start + quality;
62✔
741
                    line_ctx->west_quality = quality;
62✔
742
                    BFREE(line_ctx->input_line_used);
62✔
743
                    line_ctx->input_line_used = u32_strdup(input_line);
62✔
744
                    break;
62✔
745
                }
746
            }
747
            else if (vside == BRIG && ((size_t) (p - input_line) >= input_length - input_trailing - quality)) {
63✔
748
                if (quality > line_ctx->east_quality) {
46✔
749
                    line_ctx->east_start = (size_t) (p - input_line);
36✔
750
                    line_ctx->east_end = line_ctx->east_start + quality;
36✔
751
                    line_ctx->east_quality = quality;
36✔
752
                    BFREE(line_ctx->input_line_used);
36!
753
                    line_ctx->input_line_used = u32_strdup(input_line);
36✔
754
                    break;
36✔
755
                }
756
            }
757
        }
758
    }
759
}
100✔
760

761

762

763
static int sufficient_body_quality(remove_ctx_t *ctx)
15✔
764
{
765
    size_t num_body_lines = ctx->bottom_start_idx - ctx->top_end_idx;
15✔
766
    size_t total_quality = 0;
15✔
767
    line_ctx_t *body = ctx->body;
15✔
768
    for (size_t body_line_idx = 0; body_line_idx < num_body_lines; body_line_idx++) {
74✔
769
        line_ctx_t line_ctx = body[body_line_idx];
59✔
770
        total_quality += line_ctx.east_quality + line_ctx.west_quality;
59✔
771
    }
772

773
    size_t max_quality = (opt.design->shape[NW].width + opt.design->shape[NE].width) * num_body_lines;
15✔
774
    /* If we manage to match 50%, then it is unlikely to improve with a different comparison mode. */
775
    int sufficient = total_quality > 0.5 * max_quality;
15✔
776
    #ifdef DEBUG
777
        fprintf(stderr, "sufficient_body_quality() found body match quality of %d/%d (%s).\n",
778
                (int) total_quality, (int) max_quality, sufficient ? "sufficient" : "NOT sufficient");
779
    #endif
780
    return sufficient;
15✔
781
}
782

783

784

785
static void reset_body(remove_ctx_t *ctx)
1✔
786
{
787
    if (ctx->body != NULL) {
1!
788
        for (size_t i = 0; i < ctx->body_num_lines; i++) {
2✔
789
            BFREE(ctx->body[i].input_line_used);
1!
790
        }
791
        memset(ctx->body, 0, ctx->body_num_lines * sizeof(line_ctx_t));
1✔
792
    }
793
}
1✔
794

795

796

797
static void find_vertical_shapes(remove_ctx_t *ctx)
14✔
798
{
799
    int west_empty = ctx->empty_side[BLEF];
14✔
800
    int east_empty = ctx->empty_side[BRIG];
14✔
801
    if (west_empty && east_empty) {
14!
802
        return;
×
803
    }
804

805
    for (comparison_t comp_type = 0; comp_type < NUM_COMPARISON_TYPES; comp_type++) {
23!
806
        if (!comp_type_is_viable(comp_type, ctx->input_is_mono, ctx->design_is_mono)) {
23✔
807
            continue;
8✔
808
        }
809
        ctx->comp_type = comp_type;
15✔
810

811
        shape_line_ctx_t **shape_lines_west = NULL;
15✔
812
        if (!west_empty) {
15!
813
            shape_lines_west = prepare_comp_shapes_vert(BLEF, comp_type);
15✔
814
        }
815
        shape_line_ctx_t **shape_lines_east = NULL;
15✔
816
        if (!east_empty) {
15✔
817
            shape_lines_east = prepare_comp_shapes_vert(BRIG, comp_type);
13✔
818
        }
819

820
        for (size_t input_line_idx = ctx->top_end_idx; input_line_idx < ctx->bottom_start_idx; input_line_idx++) {
74✔
821
            size_t input_indent = 0;
59✔
822
            size_t input_trailing = 0;
59✔
823
            uint32_t *input_line = prepare_comp_input(input_line_idx, 0, comp_type, 0, &input_indent, &input_trailing);
59✔
824
            size_t input_length = u32_strlen(input_line);
59✔
825

826
            if (!west_empty) {
59!
827
                match_vertical_side(ctx, BLEF, shape_lines_west,
59✔
828
                        input_line, input_line_idx, input_length, input_indent, input_trailing);
829
            }
830
            if (!east_empty) {
59✔
831
                match_vertical_side(ctx, BRIG, shape_lines_east,
41✔
832
                        input_line, input_line_idx, input_length, input_indent, input_trailing);
833
            }
834
        }
835

836
        free_shape_lines(shape_lines_west);
15✔
837
        free_shape_lines(shape_lines_east);
15✔
838

839
        if (sufficient_body_quality(ctx)) {
15✔
840
            break;
14✔
841
        }
842
        reset_body(ctx);
1✔
843
    }
844
}
845

846

847

848
/**
849
 * If the user didn't specify a design to remove, autodetect it.
850
 * Since this requires knowledge of all available designs, the entire config file had to be parsed (earlier).
851
 */
852
static void detect_design_if_needed()
14✔
853
{
854
    if (opt.design_choice_by_user == 0) {
14✔
855
        design_t *tmp = autodetect_design();
6✔
856
        if (tmp) {
6!
857
            opt.design = tmp;
6✔
858
            #ifdef DEBUG
859
                fprintf(stderr, "Design autodetection: Removing box of design \"%s\".\n", opt.design->name);
860
            #endif
861
        }
862
        else {
863
            fprintf(stderr, "%s: Box design autodetection failed. Use -d option.\n", PROJECT);
×
864
            exit(EXIT_FAILURE);
×
865
        }
866
    }
867
    #ifdef DEBUG
868
    else {
869
        fprintf(stderr, "Design was chosen by user: %s\n", opt.design->name);
870
    }
871
    #endif
872
}
14✔
873

874

875

876
static void free_line_text(line_t *line)
151✔
877
{
878
    BFREE(line->cache_visible);
151✔
879
    bxs_free(line->text);
151✔
880
    line->text = NULL;
151✔
881
}
151✔
882

883

884

885
static void free_line(line_t *line)
93✔
886
{
887
    free_line_text(line);
93✔
888
    BFREE(line->tabpos);
93!
889
    line->tabpos_len = 0;
93✔
890
}
93✔
891

892

893

894
static void killblank(remove_ctx_t *ctx)
12✔
895
{
896
    while (ctx->top_end_idx < ctx->bottom_start_idx && empty_line(input.lines + ctx->top_end_idx)) {
19!
897
        #ifdef DEBUG
898
            fprintf(stderr, "Killing leading blank line in box body.\n");
899
        #endif
900
        ++(ctx->top_end_idx);
7✔
901
    }
902
    while (ctx->bottom_start_idx > ctx->top_end_idx && empty_line(input.lines + ctx->bottom_start_idx - 1)) {
18!
903
        #ifdef DEBUG
904
            fprintf(stderr, "Killing trailing blank line in box body.\n");
905
        #endif
906
        --(ctx->bottom_start_idx);
6✔
907
    }
908
}
12✔
909

910

911

912
static int org_is_not_blank(bxstr_t *org_line, comparison_t comp_type, size_t idx)
41✔
913
{
914
    if (comp_type == literal || comp_type == ignore_invisible_shape) {
41!
915
        return !is_blank(org_line->memory[idx]);
33✔
916
    }
917
    return !is_blank(org_line->memory[org_line->visible_char[idx]]);
8✔
918
}
919

920

921

922
static size_t max_chars_line(bxstr_t *org_line, comparison_t comp_type)
81✔
923
{
924
    return (comp_type == literal || comp_type == ignore_invisible_shape)
17✔
925
            ? org_line->num_chars : org_line->num_chars_visible;
98✔
926
}
927

928

929

930
static size_t confirmed_padding(bxstr_t *org_line, comparison_t comp_type, size_t start_idx, size_t num_padding)
58✔
931
{
932
    size_t result = 0;
58✔
933
    size_t max_chars = max_chars_line(org_line, comp_type);
58✔
934
    for (size_t i = start_idx; i < BMIN(max_chars, start_idx + num_padding); i++) {
99✔
935
        if (org_is_not_blank(org_line, comp_type, i)) {
41!
936
            break;
×
937
        }
938
        result++;
41✔
939
    }
940
    return result;
58✔
941
}
942

943

944

945
static void remove_top_from_input(remove_ctx_t *ctx)
14✔
946
{
947
    if (ctx->top_end_idx > ctx->top_start_idx) {
14✔
948
        for (size_t j = ctx->top_start_idx; j < ctx->top_end_idx; ++j) {
60✔
949
            free_line(input.lines + j);
47✔
950
        }
951
        memmove(input.lines + ctx->top_start_idx, input.lines + ctx->top_end_idx,
13✔
952
                (input.num_lines - ctx->top_end_idx) * sizeof(line_t));
13✔
953
        size_t num_lines_removed = ctx->top_end_idx - ctx->top_start_idx;
13✔
954
        input.num_lines -= num_lines_removed;
13✔
955
        ctx->bottom_start_idx -= num_lines_removed;
13✔
956
        ctx->bottom_end_idx -= num_lines_removed;
13✔
957
    }
958
}
14✔
959

960

961

962
static size_t calculate_start_idx(remove_ctx_t *ctx, size_t body_line_idx)
58✔
963
{
964
    size_t input_line_idx = ctx->top_end_idx + body_line_idx;
58✔
965
    line_ctx_t *lctx = ctx->body + body_line_idx;
58✔
966
    bxstr_t *org_line = input.lines[input_line_idx].text;
58✔
967

968
    size_t s_idx = 0;
58✔
969
    if (lctx->west_quality > 0) {
58!
970
        s_idx = lctx->west_end + confirmed_padding(org_line, ctx->comp_type, lctx->west_end, opt.design->padding[BLEF]);
58✔
971
    }
972
    if (ctx->comp_type == ignore_invisible_input || ctx->comp_type == ignore_invisible_all) {
58✔
973
        /* our line context worked with visible characters only, convert back to org_line */
974
        s_idx = org_line->first_char[s_idx];
15✔
975
    }
976
    return s_idx;
58✔
977
}
978

979

980

981
static size_t calculate_end_idx(remove_ctx_t *ctx, size_t body_line_idx)
58✔
982
{
983
    size_t input_line_idx = ctx->top_end_idx + body_line_idx;
58✔
984
    line_ctx_t *lctx = ctx->body + body_line_idx;
58✔
985
    bxstr_t *org_line = input.lines[input_line_idx].text;
58✔
986

987
    size_t e_idx = lctx->east_quality > 0 ? lctx->east_start : max_chars_line(org_line, ctx->comp_type);
58✔
988
    if (ctx->comp_type == ignore_invisible_input || ctx->comp_type == ignore_invisible_all) {
58✔
989
        e_idx = org_line->first_char[e_idx];
15✔
990
    }
991
    return e_idx;
58✔
992
}
993

994

995

996
static void remove_vertical_from_input(remove_ctx_t *ctx)
14✔
997
{
998
    for (size_t body_line_idx = 0; body_line_idx < ctx->body_num_lines; body_line_idx++) {
72✔
999
        size_t input_line_idx = ctx->top_end_idx + body_line_idx;
58✔
1000
        bxstr_t *org_line = input.lines[input_line_idx].text;
58✔
1001
        size_t s_idx = calculate_start_idx(ctx, body_line_idx);
58✔
1002
        size_t e_idx = calculate_end_idx(ctx, body_line_idx);
58✔
1003
        #ifdef DEBUG
1004
            fprintf(stderr, "remove_vertical_from_input(): body_line_idx=%d, input_line_idx=%d, s_idx=%d, e_idx=%d, "
1005
                    "input.indent=%d\n", (int) body_line_idx, (int) input_line_idx, (int) s_idx, (int) e_idx,
1006
                    (int) input.indent);
1007
        #endif
1008

1009
        bxstr_t *temp2 = bxs_substr(org_line, s_idx, e_idx);
58✔
1010
        bxstr_t *temp = bxs_prepend_spaces(temp2, input.indent);
58✔
1011
        free_line_text(input.lines + input_line_idx);
58✔
1012
        input.lines[input_line_idx].text = temp;
58✔
1013
        bxs_free(temp2);
58✔
1014
    }
1015
}
14✔
1016

1017

1018

1019
static void remove_bottom_from_input(remove_ctx_t *ctx)
14✔
1020
{
1021
    if (ctx->bottom_end_idx > ctx->bottom_start_idx) {
14✔
1022
        for (size_t j = ctx->bottom_start_idx; j < ctx->bottom_end_idx; ++j) {
59✔
1023
            free_line(input.lines + j);
46✔
1024
        }
1025
        if (ctx->bottom_end_idx < input.num_lines) {
13!
1026
            memmove(input.lines + ctx->bottom_start_idx, input.lines + ctx->bottom_end_idx,
×
1027
                    (input.num_lines - ctx->bottom_end_idx) * sizeof(line_t));
×
1028
        }
1029
        input.num_lines -= ctx->bottom_end_idx - ctx->bottom_start_idx;
13✔
1030
    }
1031
}
14✔
1032

1033

1034

1035
static void apply_results_to_input(remove_ctx_t *ctx)
14✔
1036
{
1037
    remove_vertical_from_input(ctx);
14✔
1038

1039
    if (opt.killblank) {
14✔
1040
        killblank(ctx);
12✔
1041
    }
1042
    remove_bottom_from_input(ctx);
14✔
1043
    remove_top_from_input(ctx);
14✔
1044

1045
    input.maxline = 0;
14✔
1046
    input.indent = SIZE_MAX;
14✔
1047
    for (size_t j = 0; j < input.num_lines; ++j) {
59✔
1048
        if (input.lines[j].text->num_columns > input.maxline) {
45✔
1049
            input.maxline = input.lines[j].text->num_columns;
17✔
1050
        }
1051
        if (input.lines[j].text->indent < input.indent) {
45✔
1052
            input.indent = input.lines[j].text->indent;
15✔
1053
        }
1054
    }
1055

1056
    size_t num_lines_removed = BMAX(ctx->top_end_idx - ctx->top_start_idx, (size_t) 0)
14✔
1057
            + BMAX(ctx->bottom_end_idx - ctx->bottom_start_idx, (size_t) 0);
14✔
1058
    memset(input.lines + input.num_lines, 0, num_lines_removed * sizeof(line_t));
14✔
1059

1060
    #ifdef DEBUG
1061
        print_input_lines(" (remove_box) after box removal");
1062
        fprintf(stderr, "Number of lines shrunk by %d.\n", (int) num_lines_removed);
1063
    #endif
1064
}
14✔
1065

1066

1067

1068
int remove_box()
14✔
1069
{
1070
    detect_design_if_needed();
14✔
1071

1072
    remove_ctx_t *ctx = (remove_ctx_t *) calloc(1, sizeof(remove_ctx_t));
14✔
1073
    ctx->empty_side[BTOP] = empty_side(opt.design->shape, BTOP);
14✔
1074
    ctx->empty_side[BRIG] = empty_side(opt.design->shape, BRIG);
14✔
1075
    ctx->empty_side[BBOT] = empty_side(opt.design->shape, BBOT);
14✔
1076
    ctx->empty_side[BLEF] = empty_side(opt.design->shape, BLEF);
14✔
1077
    #ifdef DEBUG
1078
        fprintf(stderr, "Empty sides? Top: %d, Right: %d, Bottom: %d, Left: %d\n",
1079
                ctx->empty_side[BTOP], ctx->empty_side[BRIG], ctx->empty_side[BBOT], ctx->empty_side[BLEF]);
1080
    #endif
1081

1082
    ctx->design_is_mono = design_is_mono(opt.design);
14✔
1083
    ctx->input_is_mono = input_is_mono();
14✔
1084

1085
    ctx->top_start_idx = find_first_line();
14✔
1086
    if (ctx->top_start_idx >= input.num_lines) {
14!
1087
        return 0;  /* all lines were already blank, so there is no box to remove */
×
1088
    }
1089

1090
    if (ctx->empty_side[BTOP]) {
14✔
1091
        ctx->top_end_idx = ctx->top_start_idx;
2✔
1092
    }
1093
    else {
1094
        ctx->top_end_idx = find_top_side(ctx);
12✔
1095
    }
1096
    #ifdef DEBUG
1097
        fprintf(stderr, "ctx->top_start_idx = %d, ctx->top_end_idx = %d\n", (int) ctx->top_start_idx,
1098
                (int) ctx->top_end_idx);
1099
    #endif
1100

1101
    ctx->bottom_end_idx = find_last_line() + 1;
14✔
1102
    if (ctx->empty_side[BBOT]) {
14✔
1103
        ctx->bottom_start_idx = ctx->bottom_end_idx;
2✔
1104
    }
1105
    else {
1106
        ctx->bottom_start_idx = find_bottom_side(ctx);
12✔
1107
    }
1108
    #ifdef DEBUG
1109
        fprintf(stderr, "ctx->bottom_start_idx = %d, ctx->bottom_end_idx = %d\n", (int) ctx->bottom_start_idx,
1110
                (int) ctx->bottom_end_idx);
1111
    #endif
1112
    if (ctx->bottom_start_idx > ctx->top_end_idx) {
14!
1113
        ctx->body_num_lines = ctx->bottom_start_idx - ctx->top_end_idx;
14✔
1114
    }
1115

1116
    if (ctx->body_num_lines > 0) {
14!
1117
        ctx->body = (line_ctx_t *) calloc(ctx->body_num_lines, sizeof(line_ctx_t));
14✔
1118
        find_vertical_shapes(ctx);
14✔
1119
    }
1120

1121
    debug_print_remove_ctx(ctx, "before apply_results_to_input()");
14✔
1122
    apply_results_to_input(ctx);
14✔
1123

1124
    if (ctx->body != NULL) {
14!
1125
        for (size_t i = 0; i < ctx->body_num_lines; i++) {
72✔
1126
            BFREE(ctx->body[i].input_line_used);
58!
1127
        }
1128
        BFREE(ctx->body);
14!
1129
    }
1130
    BFREE(ctx);
14!
1131
    return 0;
14✔
1132
}
1133

1134

1135

1136
void output_input(const int trim_only)
14✔
1137
{
1138
    size_t indent;
1139
    int ntabs, nspcs;
1140

1141
    #ifdef DEBUG
1142
        fprintf(stderr, "output_input() - enter (trim_only=%d)\n", trim_only);
1143
    #endif
1144
    for (size_t j = 0; j < input.num_lines; ++j) {
59✔
1145
        if (input.lines[j].text == NULL) {
45!
1146
            continue;
×
1147
        }
1148
        bxstr_t *temp = bxs_rtrim(input.lines[j].text);
45✔
1149
        bxs_free(input.lines[j].text);
45✔
1150
        input.lines[j].text = temp;
45✔
1151
        if (trim_only) {
45✔
1152
            continue;
8✔
1153
        }
1154

1155
        char *indentspc = NULL;
37✔
1156
        if (opt.tabexp == 'u') {
37✔
1157
            indent = input.lines[j].text->indent;
4✔
1158
            ntabs = indent / opt.tabstop;
4✔
1159
            nspcs = indent % opt.tabstop;
4✔
1160
            indentspc = (char *) malloc(ntabs + nspcs + 1);
4✔
1161
            if (indentspc == NULL) {
4!
1162
                perror(PROJECT);
×
1163
                return;
×
1164
            }
1165
            memset(indentspc, (int) '\t', ntabs);
4✔
1166
            memset(indentspc + ntabs, (int) ' ', nspcs);
4✔
1167
            indentspc[ntabs + nspcs] = '\0';
4✔
1168
        }
1169
        else if (opt.tabexp == 'k') {
33!
1170
            uint32_t *indent32 = tabbify_indent(j, NULL, input.indent);
×
1171
            indentspc = u32_strconv_to_output(indent32);
×
1172
            BFREE(indent32);
×
1173
            indent = input.indent;
×
1174
        }
1175
        else {
1176
            indentspc = (char *) strdup("");
33✔
1177
            indent = 0;
33✔
1178
        }
1179

1180
        char *outtext = u32_strconv_to_output(bxs_first_char_ptr(input.lines[j].text, indent));
37✔
1181
        fprintf(opt.outfile, "%s%s%s", indentspc, outtext,
37✔
1182
                (input.final_newline || j < input.num_lines - 1 ? opt.eol : ""));
37!
1183
        BFREE(outtext);
37!
1184
        BFREE(indentspc);
37!
1185
    }
1186
}
1187

1188

1189
/* vim: set sw=4: */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc