• 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

82.93
/src/shape.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
 * Shape handling and information functions
18
 */
19

20
#include "config.h"
21

22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <stdarg.h>
25
#include <string.h>
26

27
#include "boxes.h"
28
#include "bxstring.h"
29
#include "shape.h"
30
#include "tools.h"
31

32

33

34
char *shape_name[] = {
35
        "NW", "NNW", "N", "NNE", "NE", "ENE", "E", "ESE",
36
        "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW"
37
};
38

39
shape_t north_side[SHAPES_PER_SIDE] = {NW, NNW, N, NNE, NE};  /* clockwise */
40

41
shape_t east_side[SHAPES_PER_SIDE] = {NE, ENE, E, ESE, SE};
42

43
shape_t south_side[SHAPES_PER_SIDE] = {SE, SSE, S, SSW, SW};
44
shape_t south_side_rev[SHAPES_PER_SIDE] = {SW, SSW, S, SSE, SE};
45

46
shape_t west_side[SHAPES_PER_SIDE] = {SW, WSW, W, WNW, NW};
47

48
shape_t corners[NUM_CORNERS] = {NW, NE, SE, SW};
49

50
shape_t *sides[] = {north_side, east_side, south_side, west_side};
51

52

53

54
shape_t findshape(const sentry_t *sarr, const int num)
2,256✔
55
/*
56
 *  Find a non-empty shape and return its name
57
 *
58
 *      sarr    the shape array to check
59
 *      num     number of entries in sarr to be checked
60
 *
61
 *  RETURNS: a shape_name  on success
62
 *           num           on error (e.g. empty shape array)
63
 *
64
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
65
 */
66
{
67
    int i;
68

69
    for (i = 0; i < num; ++i) {
10,802✔
70
        if (isempty(sarr + i)) {
9,958✔
71
            continue;
8,546✔
72
        } else {
73
            break;
1,412✔
74
        }
75
    }
76

77
    return (shape_t) i;
2,256✔
78
}
79

80

81

82
int on_side(const shape_t s, const int idx)
1,060✔
83
/*
84
 *  Compute the side that shape s is on.
85
 *
86
 *      s    shape to look for
87
 *      idx  which occurence to return (0 == first, 1 == second (for corners)
88
 *
89
 *  RETURNS: side number (BTOP etc.)  on success
90
 *           NUM_SIDES                on error (e.g. idx==1 && s no corner)
91
 *
92
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
93
 */
94
{
95
    int side;
96
    int i;
97
    int found = 0;
1,060✔
98

99
    for (side = 0; side < NUM_SIDES; ++side) {
1,826!
100
        for (i = 0; i < SHAPES_PER_SIDE; ++i) {
6,224✔
101
            if (sides[side][i] == s) {
5,458✔
102
                if (found == idx) {
1,060!
103
                    return side;
1,060✔
104
                } else {
105
                    ++found;
×
106
                }
107
            }
108
        }
2,199✔
109
    }
383✔
110

111
    return NUM_SIDES;
×
112
}
530✔
113

114

115

116
int genshape(const size_t width, const size_t height, char ***chars, bxstr_t ***mbcs)
2,124✔
117
/*
118
 *  Generate a shape consisting of spaces only.
119
 *
120
 *      width   desired shape width
121
 *      height  desired shape height
122
 *      chars   pointer to the shape lines (should be NULL upon call)
123
 *      mbcs    pointer to the shape lines, MBCS version (should be NULL upon call)
124
 *
125
 *  Memory is allocated for the shape lines which must be freed by the caller.
126
 *
127
 *  RETURNS:  == 0   on success (memory allocated)
128
 *            != 0   on error   (no memory allocated)
129
 *
130
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
131
 */
132
{
133
    size_t j;
134

135
    if (width <= 0 || height <= 0 || width > LINE_MAX_BYTES) {
2,124!
136
        fprintf(stderr, "%s: internal error\n", PROJECT);
×
137
        return 1;
×
138
    }
139

140
    *chars = (char **) calloc(height, sizeof(char *));
2,124✔
141
    if (*chars == NULL) {
2,124✔
142
        perror(PROJECT);
×
143
        return 2;
×
144
    }
145

146
    *mbcs = (bxstr_t **) calloc(height, sizeof(bxstr_t *));
2,124✔
147
    if (*mbcs == NULL) {
2,124✔
148
        BFREE(*chars);
×
149
        perror(PROJECT);
×
150
        return 4;
×
151
    }
152

153
    for (j = 0; j < height; ++j) {
4,248✔
154
        (*chars)[j] = nspaces(width);
2,124✔
155
        (*mbcs)[j] = bxs_from_ascii((*chars)[j]);
2,124✔
156
    }
1,062✔
157

158
    return 0;
2,124✔
159
}
1,062✔
160

161

162

163
void freeshape(sentry_t *shape)
×
164
/*
165
 *  Free all memory allocated by the shape and set the struct to
166
 *  SENTRY_INITIALIZER. Do not free memory of the struct.
167
 *
168
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
169
 */
170
{
171
    size_t j;
172

173
    for (j = 0; j < shape->height; ++j) {
×
174
        BFREE (shape->chars[j]);
×
175
        bxs_free(shape->mbcs[j]);
×
176
    }
177
    BFREE (shape->chars);
×
178
    BFREE (shape->mbcs);
×
179

180
    *shape = SENTRY_INITIALIZER;
×
181
}
×
182

183

184

185
int isempty(const sentry_t *shape)
183,576✔
186
/*
187
 *  Return true if shape is empty.
188
 *
189
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
190
 */
191
{
192
    if (shape == NULL) {
183,576✔
193
        return 1;
×
194
    } else if (shape->chars == NULL || shape->mbcs == NULL) {
183,576!
195
        return 1;
85,292✔
196
    } else if (shape->width == 0 || shape->height == 0) {
98,284!
197
        return 1;
×
198
    } else {
199
        return 0;
98,284✔
200
    }
201
}
91,788✔
202

203

204

205
int isdeepempty(const sentry_t *shape)
13,326✔
206
/*
207
 *  Return true if shape is empty, also checking if lines consist of whitespace
208
 *  only.
209
 *
210
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
211
 */
212
{
213
    size_t j;
214

215
    if (isempty(shape)) {
13,326✔
216
        return 1;
988✔
217
    }
218

219
    for (j = 0; j < shape->height; ++j) {
18,412✔
220
        if (shape->chars[j]) {
16,926✔
221
            if (strspn(shape->chars[j], " \t") != shape->width) {
16,926✔
222
                return 0;
10,852✔
223
            }
224
        }
3,037✔
225
    }
3,037✔
226

227
    return 1;
1,486✔
228
}
6,663✔
229

230

231

232
size_t highest(const sentry_t *sarr, const int n, ...)
460✔
233
/*
234
 *  Return height (vert.) of highest shape in given list.
235
 *
236
 *  sarr         array of shapes to examine
237
 *  n            number of shapes following
238
 *  ...          the shapes to consider
239
 *
240
 *  RETURNS:     height in lines (may be zero)
241
 *
242
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
243
 */
244
{
245
    va_list ap;
246
    int i;
247
    size_t max = 0;                      /* current maximum height */
460✔
248

249
    #if defined(DEBUG) && 0
250
    fprintf (stderr, "highest (%d, ...)\n", n);
251
    #endif
252

253
    va_start (ap, n);
460✔
254

255
    for (i = 0; i < n; ++i) {
2,760✔
256
        shape_t r = va_arg (ap, shape_t);
2,300✔
257
        if (!isempty(sarr + r)) {
2,300✔
258
            if (sarr[r].height > max) {
1,446✔
259
                max = sarr[r].height;
460✔
260
            }
230✔
261
        }
723✔
262
    }
1,150✔
263

264
    va_end (ap);
460✔
265

266
    return max;
460✔
267
}
268

269

270

271
size_t widest(const sentry_t *sarr, const int n, ...)
460✔
272
/*
273
 *  Return width (horiz.) of widest shape in given list.
274
 *
275
 *  sarr         array of shapes to examine
276
 *  n            number of shapes following
277
 *  ...          the shapes to consider
278
 *
279
 *  RETURNS:     width in chars (may be zero)
280
 *
281
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
282
 */
283
{
284
    va_list ap;
285
    int i;
286
    size_t max = 0;                      /* current maximum width */
460✔
287

288
    #if defined(DEBUG) && 0
289
    fprintf (stderr, "widest (%d, ...)\n", n);
290
    #endif
291

292
    va_start (ap, n);
460✔
293

294
    for (i = 0; i < n; ++i) {
2,760✔
295
        shape_t r = va_arg (ap, shape_t);
2,300✔
296
        if (!isempty(sarr + r)) {
2,300✔
297
            if (sarr[r].width > max) {
1,420✔
298
                max = sarr[r].width;
460✔
299
            }
230✔
300
        }
710✔
301
    }
1,150✔
302

303
    va_end (ap);
460✔
304

305
    return max;
460✔
306
}
307

308

309

310
/**
311
 * Return true if the shapes on the given side consist entirely of spaces - and spaces only, tabs are considered
312
 * non-empty.
313
 *
314
 * @param sarr pointer to shape list of design to check
315
 * @param aside the box side (one of `BTOP` etc.)
316
 * @return == 0: side is not empty;
317
 *         \!= 0: side is empty
318
 */
319
int empty_side(sentry_t *sarr, const int aside)
3,450✔
320
{
321
    int i;
322

323
    for (i = 0; i < SHAPES_PER_SIDE; ++i) {
5,530✔
324
        if (isdeepempty(sarr + sides[aside][i])) {
5,194✔
325
            continue;
2,080✔
326
        } else {
327
            return 0;
3,114✔
328
        }                    /* side is not empty */
329
    }
330

331
    return 1;                            /* side is empty */
336✔
332
}
1,725✔
333

334

335

336
static int is_west(sentry_t *shape, int include_corners)
6,824✔
337
{
338
    size_t offset = include_corners ? 0 : 1;
6,824✔
339
    for (size_t i = offset; i < SHAPES_PER_SIDE - offset; i++) {
34,120✔
340
        if (west_side[i] == shape->name) {
27,296!
UNCOV
341
            return 1;
×
342
        }
343
    }
13,648✔
344
    return 0;
6,824✔
345
}
3,412✔
346

347

348

349
static int is_east(sentry_t *shape, int include_corners)
6,824✔
350
{
351
    size_t offset = include_corners ? 0 : 1;
6,824✔
352
    for (size_t i = offset; i < SHAPES_PER_SIDE - offset; i++) {
34,120✔
353
        if (east_side[i] == shape->name) {
27,296!
UNCOV
354
            return 1;
×
355
        }
356
    }
13,648✔
357
    return 0;
6,824✔
358
}
3,412✔
359

360

361

362
static int find_in_horiz(shape_t side[], shape_t shape_name)
10,128✔
363
{
364
    int result = -1;
10,128✔
365
    for (size_t i = 0; i < SHAPES_PER_SIDE; i++) {
40,212✔
366
        if (side[i] == shape_name) {
36,908✔
367
            result = i;
6,824✔
368
            break;
6,824✔
369
        }
370
    }
15,042✔
371
    return result;
10,128✔
372
}
373

374

375

376
static int find_in_north(shape_t shape_name)
6,824✔
377
{
378
    return find_in_horiz(north_side, shape_name);
6,824✔
379
}
380

381

382

383
static int find_in_south(shape_t shape_name)
3,304✔
384
{
385
    return find_in_horiz(south_side_rev, shape_name);
3,304✔
386
}
387

388

389

390
static int *new_blankward_cache(const size_t shape_height)
2,900✔
391
{
392
    int *result = (int *) calloc(shape_height, sizeof(int));
2,900✔
393
    for (size_t i = 0; i < shape_height; i++) {
14,584✔
394
        result[i] = -1;
11,684✔
395
    }
5,842✔
396
    return result;
2,900✔
397
}
398

399

400

401
int is_blank_leftward(design_t *current_design, const shape_t shape, const size_t shape_line_idx)
3,446✔
402
{
403
    if (current_design == NULL) {
3,446✔
UNCOV
404
        return 0;  /* this would be a bug */
×
405
    }
406
    sentry_t *shape_data = current_design->shape + shape;
3,446✔
407
    if (shape_line_idx >= shape_data->height) {
3,446!
UNCOV
408
        return 0;  /* this would be a bug */
×
409
    }
410
    if (shape_data->blank_leftward != NULL && shape_data->blank_leftward[shape_line_idx] >= 0) {
3,446✔
411
        return shape_data->blank_leftward[shape_line_idx];  /* cached value available */
34✔
412
    }
413
    if (shape_data->blank_leftward == NULL) {
3,412✔
414
        shape_data->blank_leftward = new_blankward_cache(shape_data->height);
1,450✔
415
    }
725✔
416

417
    int result = -1;
3,412✔
418
    if (is_west(shape_data, 1)) {
3,412!
UNCOV
419
        result = 1;
×
420
    }
421
    else if (is_east(shape_data, 0)) {
3,412!
UNCOV
422
        result = 0;
×
423
    }
424
    else {
425
        shape_t *side = north_side;
3,412✔
426
        int pos = find_in_north(shape);
3,412✔
427
        if (pos < 0) {
3,412✔
428
            side = south_side_rev;
1,652✔
429
            pos = find_in_south(shape);
1,652✔
430
        }
826✔
431
        result = 1;
3,412✔
432
        for (size_t i = 0; i < (size_t) pos; i++) {
5,726✔
433
            sentry_t *tshape = current_design->shape + side[i];
4,582✔
434
            if (tshape->mbcs != NULL && !bxs_is_blank(tshape->mbcs[shape_line_idx])) {
4,582✔
435
                result = 0;
2,268✔
436
                break;
2,268✔
437
            }
438
        }
1,157✔
439
    }
440

441
    shape_data->blank_leftward[shape_line_idx] = result;
3,412✔
442
    return result;
3,412✔
443
}
1,723✔
444

445

446
// TODO HERE is_blank_rightward() and is_blank_leftward() are nearly identical -> consolidate
447
int is_blank_rightward(design_t *current_design, const shape_t shape, const size_t shape_line_idx)
3,446✔
448
{
449
    if (current_design == NULL) {
3,446✔
UNCOV
450
        return 0;  /* this would be a bug */
×
451
    }
452
    sentry_t *shape_data = current_design->shape + shape;
3,446✔
453
    if (shape_line_idx >= shape_data->height) {
3,446!
UNCOV
454
        return 0;  /* this would be a bug */
×
455
    }
456
    if (shape_data->blank_rightward != NULL && shape_data->blank_rightward[shape_line_idx] >= 0) {
3,446✔
457
        return shape_data->blank_rightward[shape_line_idx];  /* cached value available */
34✔
458
    }
459
    if (shape_data->blank_rightward == NULL) {
3,412✔
460
        shape_data->blank_rightward = new_blankward_cache(shape_data->height);
1,450✔
461
    }
725✔
462

463
    int result = -1;
3,412✔
464
    if (is_west(shape_data, 0)) {
3,412!
UNCOV
465
        result = 0;
×
466
    }
467
    else if (is_east(shape_data, 1)) {
3,412!
UNCOV
468
        result = 1;
×
469
    }
470
    else {
471
        shape_t *side = north_side;
3,412✔
472
        int pos = find_in_north(shape);
3,412✔
473
        if (pos < 0) {
3,412✔
474
            side = south_side_rev;
1,652✔
475
            pos = find_in_south(shape);
1,652✔
476
        }
826✔
477
        result = 1;
3,412✔
478
        for (size_t i = (size_t) pos + 1; i < SHAPES_PER_SIDE; i++) {
6,422✔
479
            sentry_t *tshape = current_design->shape + side[i];
5,294✔
480
            if (tshape->mbcs != NULL && !bxs_is_blank(tshape->mbcs[shape_line_idx])) {
5,294✔
481
                result = 0;
2,284✔
482
                break;
2,284✔
483
            }
484
        }
1,505✔
485
    }
486

487
    shape_data->blank_rightward[shape_line_idx] = result;
3,412✔
488
    return result;
3,412✔
489
}
1,723✔
490

491

492

UNCOV
493
void debug_print_shape(sentry_t *shape)
×
494
{
495
    #ifdef DEBUG
496
        if (shape == NULL) {
497
            fprintf(stderr, "NULL\n");
498
            return;
499
        }
500
        fprintf(stderr, "Shape %3s (%dx%d): elastic=%s, bl=",
501
            shape_name[shape->name], (int) shape->width, (int) shape->height, shape->elastic ? "true" : "false");
502
        if (shape->blank_leftward == NULL) {
503
            fprintf(stderr, "NULL");
504
        }
505
        else {
506
            fprintf(stderr, "[");
507
            for (size_t i = 0; i < shape->height; i++) {
508
                fprintf(stderr, "%d%s", shape->blank_leftward[i],
509
                        shape->height > 0 && i < (shape->height - 1) ? ", " : "");
510
            }
511
            fprintf(stderr, "]");
512
        }
513
        fprintf(stderr, ", br=");
514
        if (shape->blank_rightward == NULL) {
515
            fprintf(stderr, "NULL");
516
        }
517
        else {
518
            fprintf(stderr, "[");
519
            for (size_t i = 0; i < shape->height; i++) {
520
                fprintf(stderr, "%d%s", shape->blank_rightward[i],
521
                        shape->height > 0 && i < (shape->height - 1) ? ", " : "");
522
            }
523
            fprintf(stderr, "]");
524
        }
525
        fprintf(stderr, ", ascii=");
526
        if (shape->chars == NULL) {
527
            fprintf(stderr, "NULL");
528
        }
529
        else {
530
            fprintf(stderr, "[");
531
            for (size_t i = 0; i < shape->height; i++) {
532
                fprintf(stderr, "%s%s%s%s", shape->chars[i] != NULL ? "\"" : "", shape->chars[i],
533
                        shape->chars[i] != NULL ? "\"" : "", (int) i < ((int) shape->height) - 1 ? ", " : "");
534
            }
535
            fprintf(stderr, "]");
536
        }
537
        fprintf(stderr, ", mbcs=");
538
        if (shape->mbcs == NULL) {
539
            fprintf(stderr, "NULL");
540
        }
541
        else {
542
            fprintf(stderr, "[");
543
            for (size_t i = 0; i < shape->height; i++) {
544
                char *out_mbcs = bxs_to_output(shape->mbcs[i]);
545
                fprintf(stderr, "%s%s%s%s", shape->mbcs[i] != NULL ? "\"" : "", out_mbcs,
546
                        shape->mbcs[i] != NULL ? "\"" : "", shape->height > 0 && i < (shape->height - 1) ? ", " : "");
547
                BFREE(out_mbcs);
548
            }
549
            fprintf(stderr, "]");
550
        }
551
        fprintf(stderr, "\n");
552
    #else
553
        UNUSED(shape);
554
    #endif
UNCOV
555
}
×
556

557

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