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

ascii-boxes / boxes / 7079268659

03 Dec 2023 08:44PM UTC coverage: 88.225% (-0.005%) from 88.23%
7079268659

push

github

tsjensen
Restore indentation mode on box removal

2798 of 3362 branches covered (0.0%)

Branch coverage included in aggregate %.

8 of 8 new or added lines in 1 file covered. (100.0%)

127 existing lines in 5 files now uncovered.

4575 of 4995 relevant lines covered (91.59%)

25749.89 hits per line

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

96.55
/src/detect.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
 * Autodetect design used by box in input.
18
 *
19
 * When detecting box shapes in input lines, we can find (in order of precedence):
20
 * 1. (box design: colored, input: colored)
21
 *    Colored boxes which are colored as per their design. In that case, matching all the invisible characters
22
 *    increases our confidence that we have found the right shape.
23
 *    -> use both shapes and input as-is, comparison type `literal`
24
 * 2. (box design: monochrome, input: monochrome)
25
 *    Boxes which have no invisible characters because color was never involved. This one is the classic case.
26
 *    -> use both shapes and input as-is, comparison type `literal` (same as 1.)
27
 * 3. (box design: monochrome, input: colored)
28
 *    Colored boxes which are colored because of lolcat processing or similar. In that case, we can ignore the
29
 *    invisible characters in the input.
30
 *    -> use shapes as-is, and ignore invisible characters in input, comparison type `ignore_invisible_input`
31
 * 4. (box design: colored, input: monochrome)
32
 *    Boxes which have no invisible characters because they have been removed (for example via --no-color), even though
33
 *    the original design was colored. In that case, we must ignore the invisible characters in the design.
34
 *    -> ignore invisible characters in shapes, use input as-is, comparison type `ignore_invisible_shape`
35
 * 5. Fallback: We assume to never see a colored box design PLUS lolcat-induced codes, or a case where a colored box
36
 *    design had its color removed and replaced with lolcat colors. That's just messy and we will treat it as case
37
 *    number two, where we ignore colors on both input and box design.
38
 *    -> ignore invisible characters in both shapes and input, comparison type `ignore_invisible_all`
39
 */
40

41
#include "config.h"
42

43
#include <unistr.h>
44
#include <unitypes.h>
45

46
#include "boxes.h"
47
#include "bxstring.h"
48
#include "shape.h"
49
#include "tools.h"
50
#include "unicode.h"
51
#include "detect.h"
52

53

54

55
char *comparison_name[] = {
56
        "literal", "ignore_invisible_input", "ignore_invisible_shape", "ignore_invisible_all"
57
};
58

59

60

61
int input_is_mono()
48✔
62
{
63
    int result = 1;
48✔
64
    for (size_t line_no = 0; line_no < input.num_lines; line_no++) {
304✔
65
        if (input.lines[line_no].text->num_chars_invisible > 0) {
282✔
66
            result = 0;
26✔
67
            break;
26✔
68
        }
69
    }
128✔
70
    #ifdef DEBUG
71
        fprintf(stderr, "Input is %s\n", result ? "mono" : "potentially colored");
72
    #endif
73
    return result;
48✔
74
}
75

76

77

78
int design_is_mono(design_t *design)
1,196✔
79
{
80
    for (shape_t scnt = 0; scnt < NUM_SHAPES; ++scnt) {
19,132✔
81
        if (isempty(design->shape + scnt)) {
18,012✔
82
            continue;
6,860✔
83
        }
84
        for (size_t line_no = 0; line_no < design->shape[scnt].height; line_no++) {
41,336✔
85
            bxstr_t *shape_line = design->shape[scnt].mbcs[line_no];
30,260✔
86
            if (shape_line->num_chars_invisible > 0) {
30,260✔
87
                #ifdef DEBUG
88
                    fprintf(stderr, "Design is potentially colored\n");
89
                #endif
90
                return 0;
76✔
91
            }
92
        }
15,092✔
93
    }
5,538✔
94
    #ifdef DEBUG
95
        fprintf(stderr, "Design is mono\n");
96
    #endif
97
    return 1;
1,120✔
98
}
598✔
99

100

101

102
int comp_type_is_viable(comparison_t comp_type, int mono_input, int mono_design)
1,604✔
103
{
104
    int result = 1;
1,604✔
105
    if ((comp_type == literal && mono_input != mono_design)
1,621✔
106
            || (comp_type == ignore_invisible_input && (mono_input || !mono_design))
1,428✔
107
            || (comp_type == ignore_invisible_shape && (!mono_input || mono_design))
1,089✔
108
            || (comp_type == ignore_invisible_all && (mono_input || mono_design)))
994!
109
    {
110
        result = 0;
834✔
111
    }
417✔
112
    return result;
1,604✔
113
}
114

115

116

117
static int *determine_empty_sides(design_t *current_design)
514✔
118
{
119
    int *result = (int *) calloc(NUM_SIDES, sizeof(int));
514✔
120

121
    for (size_t j = 0; j < NUM_SIDES; ++j) {
2,570✔
122
        result[j] = empty_side(current_design->shape, j);
2,056✔
123
    }
1,028✔
124
    #ifdef DEBUG
125
        fprintf (stderr, "Empty sides: TOP %d, LEFT %d, BOTTOM %d, RIGHT %d\n",
126
            result[BTOP], result[BLEF], result[BBOT], result[BRIG]);
127
    #endif
128
    return result;
514✔
129
}
130

131

132

133
static uint32_t *get_visible_text(line_t *line)
3,692✔
134
{
135
    uint32_t *result = NULL;
3,692✔
136
    if (line != NULL) {
3,692✔
137
        if (line->cache_visible == NULL) {
3,692✔
138
            line->cache_visible = bxs_filter_visible(line->text);
112✔
139
        }
56✔
140
        result = line->cache_visible;
3,692✔
141
    }
1,846✔
142
    return result;
3,692✔
143
}
144

145

146

147
uint32_t *prepare_comp_shape(
13,646✔
148
        design_t *design, shape_t shape, size_t shape_line_idx, comparison_t comp_type, int trim_left, int trim_right)
149
{
150
    sentry_t shape_def = design->shape[shape];
13,646✔
151
    if (shape_line_idx >= shape_def.height) {
13,646!
152
        bx_fprintf(stderr, "%s: prepare_comp_shape(\"%s\", %s, %d, %s, %d, %d): Index out of bounds\n", PROJECT,
×
153
                design->name, shape, (int) shape_line_idx, comparison_name[comp_type], trim_left, trim_right);
154
        return NULL;
×
155
    }
156

157
    bxstr_t *shape_line = shape_def.mbcs[shape_line_idx];
13,646✔
158
    uint32_t *result = NULL;
13,646✔
159

160
    if ((comp_type == ignore_invisible_shape || comp_type == ignore_invisible_all)
13,646✔
161
            && shape_line->num_chars_invisible > 0)
7,015✔
162
    {
94✔
163
        size_t ltrim = trim_left ? shape_line->indent : 0;
188✔
164
        uint32_t *visible = bxs_filter_visible(shape_line);
188✔
165
        result = u32_strdup(visible + ltrim);
188✔
166
        BFREE(visible);
188✔
167

168
        if (trim_right && shape_line->trailing > 0) {
188✔
169
            set_char_at(result, shape_line->num_chars_visible - ltrim - shape_line->trailing, char_nul);
34✔
170
        }
17✔
171
    }
94✔
172
    else {
173
        result = u32_strdup(trim_left ? bxs_unindent_ptr(shape_line) : shape_line->memory);
13,458✔
174

175
        if (trim_right && shape_line->trailing > 0) {
13,458✔
176
            size_t x = shape_line->num_chars_visible - (trim_left ? shape_line->indent : 0) - shape_line->trailing;
1,730✔
177
            set_char_at(result, shape_line->first_char[x], char_nul);
1,730✔
178
        }
865✔
179
    }
180
    return result;
13,646✔
181
}
6,823✔
182

183

184

185
uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t comp_type, size_t offset_right,
27,830✔
186
    size_t *out_indent, size_t *out_trailing)
187
{
188
    #ifdef DEBUG
189
        fprintf(stderr, "prepare_comp_input(%d, %s, %s, %d, %p, %p)", (int) input_line_idx,
190
                trim_left ? "true" : "false", comparison_name[comp_type], (int) offset_right, out_indent, out_trailing);
191
    #endif
192
    if (input_line_idx >= input.num_lines) {
27,830!
193
        bx_fprintf(stderr, "%s: prepare_comp_input(%d, %d, %s, %d): Index out of bounds\n", PROJECT,
×
194
                (int) input_line_idx, trim_left, comparison_name[comp_type], (int) offset_right);
195
        #ifdef DEBUG
196
            fprintf(stderr, " -> (null)\n");
197
        #endif
198
        return NULL;
×
199
    }
200
    bxstr_t *input_line = input.lines[input_line_idx].text;
27,830✔
201

202
    uint32_t *result = NULL;
27,830✔
203
    if (comp_type == ignore_invisible_input || comp_type == ignore_invisible_all) {
27,830✔
204
        if (offset_right > 0) {
3,692✔
205
            uint32_t *visible = get_visible_text(input.lines + input_line_idx);
740✔
206
            uint32_t *p = visible + input_line->num_chars_visible - input_line->trailing - offset_right;
740✔
207
            if (p >= visible) {
740✔
208
                result = p;
740✔
209
                if (out_indent != NULL) {
740✔
210
                    *out_indent = BMAX(input_line->indent - (size_t) (p - visible), (size_t) 0);
×
211
                }
212
            }
370✔
213
        }
370✔
214
        else {
215
            size_t ltrim = trim_left ? input_line->indent : 0;
2,952✔
216
            result = get_visible_text(input.lines + input_line_idx) + ltrim;
2,952✔
217
            if (out_indent != NULL) {
2,952✔
218
                *out_indent = trim_left ? 0 : input_line->indent;
30✔
219
            }
15✔
220
        }
221
        if (out_trailing != NULL) {
3,692✔
222
            *out_trailing = input_line->trailing;
30✔
223
        }
15✔
224
    }
1,846✔
225
    else {
226
        if (offset_right > 0) {
24,138✔
227
            int idx = (int) input_line->first_char[input_line->num_chars_visible - input_line->trailing]
10,845✔
228
                        - offset_right;
7,230✔
229
            if (idx >= 0) {
7,230✔
230
                result = input_line->memory + idx;
7,152✔
231
                if (out_indent != NULL) {
7,152✔
232
                    *out_indent = BMAX(input_line->first_char[input_line->indent] - (size_t) idx, (size_t) 0);
×
233
                }
234
            }
3,576✔
235
        }
3,615✔
236
        else {
237
            result = trim_left ? bxs_unindent_ptr(input_line) : input_line->memory;
16,908✔
238
            if (out_indent != NULL) {
16,908✔
239
                *out_indent = trim_left ? 0 : input_line->first_char[input_line->indent];
92✔
240
            }
46✔
241
        }
242
        if (out_trailing != NULL) {
24,138✔
243
            *out_trailing = input_line->num_chars
138✔
244
                    - input_line->first_char[input_line->num_chars_visible - input_line->trailing];
92✔
245
        }
46✔
246
    }
247
    #ifdef DEBUG
248
        char *out_result = u32_strconv_to_output(result);
249
        fprintf(stderr, " -> \"%s\"\n", out_result);
250
        BFREE(out_result);
251
    #endif
252
    return result;
27,830✔
253
}
13,915✔
254

255

256

257
/**
258
 * Try and find west corner shapes. Every non-empty shape line is searched for on every input line. A hit is generated
259
 * whenever a match is found.
260
 * @param current_design the current design to check
261
 * @param comp_type the comparison type (how to compare colored strings)
262
 * @param empty information on which box sides are empty in that design
263
 * @param corner which west corner to search for
264
 * @return the number of hits for this corner
265
 */
266
static size_t find_west_corner(design_t *current_design, comparison_t comp_type, int *empty, shape_t corner)
1,100✔
267
{
268
    size_t hits = 0;
1,100✔
269
    if (empty[BLEF] || (empty[BTOP] && corner == NW) || (empty[BBOT] && corner == SW)) {
1,100!
270
        return hits;
168✔
271
    }
272

273
    for (size_t j = 0; j < current_design->shape[corner].height; ++j) {
3,496✔
274
        bxstr_t *shape_line = current_design->shape[corner].mbcs[j];
2,556✔
275
        if (bxs_is_blank(shape_line)) {
2,556✔
276
            continue;
1,232✔
277
        }
278

279
        uint32_t *shape_relevant = prepare_comp_shape(current_design, corner, j, comp_type, 1, 0);
1,324✔
280
        size_t length_relevant = u32_strlen(shape_relevant);
1,324✔
281

282
        for (size_t k = 0; k < current_design->shape[corner].height; ++k) {
4,570✔
283
            size_t a = k;
3,576✔
284
            if (corner == SW) {
3,576✔
285
                a += input.num_lines - current_design->shape[corner].height;
1,630✔
286
            }
815✔
287
            if (a >= input.num_lines) {
3,576✔
288
                break;
330✔
289
            }
290

291
            uint32_t *input_relevant = prepare_comp_input(a, 1, comp_type, 0, NULL, NULL);
3,246✔
292
            if (u32_strncmp(input_relevant, shape_relevant, length_relevant) == 0) {
3,246✔
293
                ++hits; /* CHECK more hit points for longer matches, or simple boxes might match too easily */
200✔
294
            }
100✔
295
        }
1,623✔
296
        BFREE(shape_relevant);
1,324✔
297
    }
662✔
298

299
    #ifdef DEBUG
300
        fprintf(stderr, "Checking %s corner produced %d hits.\n", shape_name[corner], (int) hits);
301
    #endif
302
    return hits;
860✔
303
}
514✔
304

305

306

307
/**
308
 * Try and find east corner shapes. Every non-empty shape line is searched for on every input line. A hit is generated
309
 * whenever a match is found.
310
 * @param current_design the current design to check
311
 * @param comp_type the comparison type (how to compare colored strings)
312
 * @param empty information on which box sides are empty in that design
313
 * @param corner which west corner to search for
314
 * @return the number of hits for this corner
315
 */
316
static size_t find_east_corner(design_t *current_design, comparison_t comp_type, int *empty, shape_t corner)
1,028✔
317
{
318
    size_t hits = 0;
1,028✔
319
    if (empty[BRIG] || (empty[BTOP] && corner == NE) || (empty[BBOT] && corner == SE)) {
1,028!
320
        #ifdef DEBUG
321
            fprintf(stderr, "Checking %s corner produced %d hits.\n", shape_name[corner], (int) hits);
322
        #endif
323
        return hits;
224✔
324
    }
325

326
    for (size_t j = 0; j < current_design->shape[corner].height; ++j) {
3,304✔
327
        bxstr_t *shape_line = current_design->shape[corner].mbcs[j];
2,500✔
328
        if (bxs_is_blank(shape_line)) {
2,500✔
329
            continue;
1,120✔
330
        }
331

332
        uint32_t *shape_relevant = prepare_comp_shape(current_design, corner, j, comp_type, 0, 1);
1,380✔
333
        size_t length_relevant = u32_strlen(shape_relevant);
1,380✔
334

335
        for (size_t k = 0; k < current_design->shape[corner].height; ++k) {
5,112✔
336
            size_t a = k;
4,126✔
337
            if (corner == SE) {
4,126✔
338
                a += input.num_lines - current_design->shape[corner].height;
2,192✔
339
            }
1,096✔
340
            if (a >= input.num_lines) {
4,126✔
341
                break;
394✔
342
            }
343

344
            uint32_t *input_relevant = prepare_comp_input(a, 0, comp_type, length_relevant, NULL, NULL);
3,732✔
345
            if (input_relevant && (u32_strncmp(input_relevant, shape_relevant, length_relevant) == 0)) {
3,732✔
346
                ++hits; /* CHECK more hit points for longer matches, or simple boxes might match too easily */
138✔
347
            }
69✔
348
        }
1,866✔
349
        BFREE(shape_relevant);
1,380✔
350
    }
690✔
351

352
    #ifdef DEBUG
353
        fprintf(stderr, "Checking %s corner produced %d hits.\n", shape_name[corner], (int) hits);
354
    #endif
355
    return hits;
804✔
356
}
514✔
357

358

359

360
/**
361
 * Try and find a horizontal shape between the box corners. Every non-empty shape line is searched for on every input
362
 * line. Elastic shapes must occur twice in an uninterrupted row to generate a hit.
363
 * @param current_design the current design to check
364
 * @param comp_type the comparison type (how to compare colored strings)
365
 * @param empty information on which box sides are empty in that design
366
 * @param hshape which horizontal shape to search for
367
 * @return the number of hits for this horizontal shape
368
 */
369
static size_t find_horizontal_shape(design_t *current_design, comparison_t comp_type, int *empty, shape_t hshape)
3,084✔
370
{
371
    size_t hits = 0;
3,084✔
372
    if (empty[BTOP] || empty[BBOT]) {
3,084✔
373
        /* horizontal box part is empty */
374
        #ifdef DEBUG
375
            fprintf(stderr, "Checking %-3s shape produced %d hits.\n", shape_name[hshape], (int) hits);
376
        #endif
377
        return hits;
528✔
378
    }
379

380
    for (size_t j = 0; j < current_design->shape[hshape].height; ++j) {
8,554✔
381
        bxstr_t *shape_line = current_design->shape[hshape].mbcs[j];
5,998✔
382
        if (bxs_is_blank(shape_line)) {
5,998✔
383
            continue;
2,552✔
384
        }
385

386
        uint32_t *shape_relevant = prepare_comp_shape(current_design, hshape, j, comp_type,
5,169✔
387
                is_blank_leftward(current_design, hshape, j),
1,723✔
388
                is_blank_rightward(current_design, hshape, j));
1,723✔
389
        size_t length_relevant = u32_strlen(shape_relevant);
3,446✔
390

391
        for (size_t k = 0; k < current_design->shape[hshape].height; ++k) {
15,520✔
392
            size_t line_idx = k;
13,322✔
393
            if (hshape >= SSE && hshape <= SSW) {
13,322✔
394
                line_idx += input.num_lines - current_design->shape[hshape].height;
5,602✔
395
            }
2,801✔
396
            if (line_idx >= input.num_lines) {
13,322✔
397
                break;
1,248✔
398
            }
399

400
            uint32_t *input_relevant = prepare_comp_input(line_idx, 1, comp_type, 0, NULL, NULL);
12,074✔
401
            uint32_t *p = u32_strstr(input_relevant, shape_relevant);
12,074✔
402
            if (p) {
12,074✔
403
                if (current_design->shape[hshape].elastic) {
1,328✔
404
                    p += length_relevant;
1,002✔
405
                    if (p - input_relevant >= (long) u32_strlen(input_relevant)) {
1,002!
UNCOV
406
                        continue;
×
407
                    }
408
                    if (u32_strncmp(p, shape_relevant, length_relevant) == 0) {
1,002✔
409
                        ++hits;
598✔
410
                    }
299✔
411
                }
501✔
412
                else {
413
                    ++hits;
326✔
414
                }
415
            }
664✔
416
        }
6,037✔
417
        BFREE(shape_relevant);
3,446✔
418
    }
1,723✔
419

420
    #ifdef DEBUG
421
        fprintf(stderr, "Checking %-3s shape produced %d hits.\n", shape_name[hshape], (int) hits);
422
    #endif
423
    return hits;
2,556✔
424
}
1,542✔
425

426

427

428
/**
429
 * Iterate over all input lines except for potential top and bottom box parts. Check if west line starts match a
430
 * non-empty shape line. If so, generate a hit.
431
 * @param current_design the current design to check
432
 * @param comp_type the comparison type (how to compare colored strings)
433
 * @param empty information on which box sides are empty in that design
434
 * @param vshape which vertical shape to search for
435
 * @return the number of hits for this vertical shape
436
 */
437
static size_t find_vertical_west(design_t *current_design, comparison_t comp_type, int *empty, shape_t vshape)
1,542✔
438
{
439
    size_t hits = 0;
1,542✔
440
    if (((empty[BTOP] ? 0 : current_design->shape[NW].height) + (empty[BBOT] ? 0 : current_design->shape[SW].height))
1,542✔
441
            >= input.num_lines) {
1,542✔
442
        /* no hits */
443
    }
252✔
444
    else if (isempty(current_design->shape + vshape)) {
1,038✔
445
        /* no hits */
446
    }
307✔
447
    else {
448
        for (size_t k = empty[BTOP] ? 0 : current_design->shape[NW].height;
4,832✔
449
                k < input.num_lines - (empty[BBOT] ? 0 : current_design->shape[SW].height); ++k)
4,620✔
450
        {
451
            uint32_t *input_relevant = prepare_comp_input(k, 1, comp_type, 0, NULL, NULL);
4,196✔
452

453
            for (size_t j = 0; j < current_design->shape[vshape].height; ++j) {
9,714✔
454
                bxstr_t *shape_line = current_design->shape[vshape].mbcs[j];
5,876✔
455
                if (bxs_is_blank(shape_line)) {
5,876✔
456
                    continue;
82✔
457
                }
458

459
                uint32_t *shape_relevant = prepare_comp_shape(current_design, vshape, j, comp_type, 1, 0);
5,794✔
460
                size_t length_relevant = u32_strlen(shape_relevant);
5,794✔
461

462
                if (u32_strncmp(input_relevant, shape_relevant, length_relevant) == 0) {
5,794✔
463
                    ++hits;
358✔
464
                    break;
358✔
465
                }
466
                BFREE(shape_relevant);
5,436✔
467
            }
2,718✔
468
        }
2,098✔
469
    }
470

471
    #ifdef DEBUG
472
        fprintf(stderr, "Checking %-3s shape produced %d hits.\n", shape_name[vshape], (int) hits);
473
    #endif
474
    return hits;
1,542✔
475
}
476

477

478

479
/**
480
 * Iterate over all input lines except for potential top and bottom box parts. Check if east line ends match a
481
 * non-empty shape line. If so, generate a hit.
482
 * @param current_design the current design to check
483
 * @param comp_type the comparison type (how to compare colored strings)
484
 * @param empty information on which box sides are empty in that design
485
 * @param vshape which vertical shape to search for
486
 * @return the number of hits for this vertical shape
487
 */
488
static size_t find_vertical_east(design_t *current_design, comparison_t comp_type, int *empty, shape_t vshape)
1,542✔
489
{
490
    size_t hits = 0;
1,542✔
491
    if (((empty[BTOP] ? 0 : current_design->shape[NW].height) + (empty[BBOT] ? 0 : current_design->shape[SW].height))
1,542✔
492
            >= input.num_lines) {
1,542✔
493
        /* no hits */
494
    }
252✔
495
    else if (isempty(current_design->shape + vshape)) {
1,038✔
496
        /* no hits */
497
    }
307✔
498
    else {
499
        for (size_t j = 0; j < current_design->shape[vshape].height; ++j) {
1,040✔
500
            bxstr_t *shape_line = current_design->shape[vshape].mbcs[j];
616✔
501
            if (bxs_is_blank(shape_line)) {
616✔
502
                continue;
92✔
503
            }
504

505
            uint32_t *shape_relevant = prepare_comp_shape(current_design, vshape, j, comp_type, 1, 1);
524✔
506
            size_t length_relevant = u32_strlen(shape_relevant);
524✔
507

508
            for (size_t k = empty[BTOP] ? 0 : current_design->shape[NW].height;
4,904✔
509
                    k < input.num_lines - (empty[BBOT] ? 0 : current_design->shape[SW].height); ++k)
4,642✔
510
            {
511
                uint32_t *input_relevant = prepare_comp_input(k, 0, comp_type, length_relevant, NULL, NULL);
4,238✔
512
                if (input_relevant != NULL && u32_strncmp(input_relevant, shape_relevant, length_relevant) == 0) {
4,238✔
513
                    ++hits;
120✔
514
                    break;
120✔
515
                }
516
            }
2,059✔
517
            BFREE(shape_relevant);
524✔
518
        }
262✔
519
    }
520

521
    #ifdef DEBUG
522
        fprintf(stderr, "Checking %-3s shape produced %d hits.\n", shape_name[vshape], (int) hits);
523
    #endif
524
    return hits;
1,542✔
525
}
526

527

528

529
static long match_design(design_t *current_design, comparison_t comp_type)
514✔
530
{
531
    int *empty = determine_empty_sides(current_design);
514✔
532
    long hits = 0;
514✔
533

534
    for (shape_t scnt = 0; scnt < NUM_SHAPES; ++scnt) {
8,738✔
535
        switch (scnt) {
8,224✔
536
            case NW:
514✔
537
            case SW:
538
                hits += find_west_corner(current_design, comp_type, empty, scnt);
1,028✔
539
                break;
1,028✔
540

541
            case NE:
514✔
542
            case SE:
543
                hits += find_east_corner(current_design, comp_type, empty, scnt);
1,028✔
544
                break;
1,028✔
545

546
            case NNW: case N: case NNE:
1,542✔
547
            case SSE: case S: case SSW:
548
                hits += find_horizontal_shape(current_design, comp_type, empty, scnt);
3,084✔
549
                break;
3,084✔
550

551
            case ENE: case E: case ESE:
771✔
552
                hits += find_vertical_east(current_design, comp_type, empty, scnt);
1,542✔
553
                break;
1,542✔
554

555
            case WSW: case W: case WNW:
771✔
556
                hits += find_vertical_west(current_design, comp_type, empty, scnt);
1,542✔
557
                break;
1,542✔
558

559
            default:
560
                fprintf(stderr, "%s: internal error (scnt=%d)\n", PROJECT, (int) scnt);
×
UNCOV
561
                return 0;
×
562
        }
563
    }
4,112✔
564

565
    BFREE(empty);
514✔
566
    return hits;
514✔
567
}
257✔
568

569

570

571
design_t *autodetect_design()
16✔
572
{
573
    design_t *current_design;           /* ptr to currently tested design */
574
    long maxhits = 0;                   /* maximum no. of hits so far */
16✔
575
    design_t *result = NULL;            /* ptr to design with the most hits */
16✔
576
    int mono_input = input_is_mono();
16✔
577
    (void) comparison_name;             /* used only in debug statements */
578

579
    for (comparison_t comp_type = 0; comp_type < NUM_COMPARISON_TYPES; comp_type++) {
32✔
580
        current_design = designs;
30✔
581
        for (size_t dcnt = 0; ((int) dcnt) < num_designs; ++dcnt, ++current_design) {
1,194✔
582
            int mono_design = design_is_mono(current_design);
1,164✔
583
            if (!comp_type_is_viable(comp_type, mono_input, mono_design)) {
1,164✔
584
                #ifdef DEBUG
585
                    fprintf(stderr, "Design \"%s\" skipped for comparison type '%s' because mono_input=%d and "
586
                        "mono_design=%d\n", current_design->name, comparison_name[comp_type], mono_input, mono_design);
587
                #endif
588
                continue;
650✔
589
            }
590

591
            #ifdef DEBUG
592
                fprintf(stderr, "CONSIDERING DESIGN ---- \"%s\" ---------------\n", current_design->name);
593
                fprintf(stderr, "    comparison_type = %s\n", comparison_name[comp_type]);
594
            #endif
595
            long hits = match_design(current_design, comp_type);
514✔
596
            #ifdef DEBUG
597
                fprintf(stderr, "Design \"%s\" scored %ld points\n", current_design->name, hits);
598
            #endif
599
            if (hits > maxhits) {
514✔
600
                maxhits = hits;
34✔
601
                result = current_design;
34✔
602
            }
17✔
603
        }
257✔
604
        if (maxhits > 2) {
30✔
605
            break;   /* do not try other comparison types if one found something */
14✔
606
        }
607
    }
8✔
608

609
    #ifdef DEBUG
610
        if (result) {
611
            fprintf(stderr, "CHOOSING \"%s\" design (%ld hits).\n", result->name, maxhits);
612
        }
613
        else {
614
            fprintf(stderr, "NO DESIGN FOUND WITH EVEN ONE HIT POINT!\n");
615
        }
616
    #endif
617
    return result;
16✔
618
}
619

620

621
/* vim: set cindent 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