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

ascii-boxes / boxes / 7314850954

24 Dec 2023 01:12PM UTC coverage: 88.826% (+2.5%) from 86.336%
7314850954

push

github

tsjensen
Use -ggdb3 option for more detailed debug info

2869 of 3408 branches covered (0.0%)

Branch coverage included in aggregate %.

4619 of 5022 relevant lines covered (91.98%)

202175.9 hits per line

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

91.06
/src/boxes.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
#include "config.h"
17

18
#ifndef __MINGW32__
19
#include <ncurses.h>
20
#endif
21
#include <locale.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <uniconv.h>
26
#include <unistd.h>
27

28
#include "boxes.h"
29
#include "bxstring.h"
30
#include "cmdline.h"
31
#include "discovery.h"
32
#include "generate.h"
33
#include "input.h"
34
#include "list.h"
35
#include "parsing.h"
36
#include "query.h"
37
#include "remove.h"
38
#include "shape.h"
39
#include "tools.h"
40
#include "unicode.h"
41

42

43

44
/*       _\|/_
45
         (o o)
46
 +----oOO-{_}-OOo------------------------------------------------------------+
47
 |                    G l o b a l   V a r i a b l e s                        |
48
 +--------------------------------------------------------------------------*/
49

50
design_t *designs = NULL;            /* available box designs */
51
int num_designs = 0;                 /* number of designs after parsing */
52

53
opt_t opt;                           /* command line options */
54

55
input_t input;                       /* input lines */
56

57
int color_output_enabled;            /* Flag indicating if ANSI color codes should be printed (1) or not (0) */
58

59

60

61
/*       _\|/_
62
         (o o)
63
 +----oOO-{_}-OOo------------------------------------------------------------+
64
 |                           F u n c t i o n s                               |
65
 +--------------------------------------------------------------------------*/
66

67

68
static int build_design(design_t **adesigns, const char *cld)
8✔
69
/*
70
 *  Build a box design.
71
 *
72
 *      adesigns    Pointer to global designs list
73
 *      cld         the W shape as specified on the command line
74
 *
75
 *  Builds the global design list containing only one design which was
76
 *  built from the -c command line definition.
77
 *
78
 *  RETURNS:  != 0   on error (out of memory)
79
 *            == 0   on success
80
 *
81
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
82
 */
83
{
84
    design_t *dp;                        /* pointer to design to be created */
85
    sentry_t *c;                         /* pointer to current shape */
86
    int i;
87
    int rc;
88

89
    *adesigns = (design_t *) calloc(1, sizeof(design_t));
8✔
90
    if (*adesigns == NULL) {
8✔
91
        perror(PROJECT);
×
92
        return 1;
×
93
    }
94
    dp = *adesigns;                      /* for readability */
8✔
95

96
    dp->name = "<Command Line Definition>";
8✔
97
    dp->aliases = (char **) calloc(1, sizeof(char *));
8✔
98
    dp->created = bxs_from_ascii("now");
8✔
99
    dp->revision = "1.0";
8✔
100
    dp->sample = bxs_from_ascii("n/a");
8✔
101
    dp->indentmode = DEF_INDENTMODE;
8✔
102
    dp->padding[BLEF] = 1;
8✔
103
    dp->defined_in = bxs_from_ascii("(command line)");
8✔
104

105
    dp->tags = (char **) calloc(2, sizeof(char *));
8✔
106
    dp->tags[0] = "transient";
8✔
107

108
    uint32_t *cld_u32 = u32_strconv_from_arg(cld, "UTF-8");  /* CHECK wrong on Windows (UTF-16) or different IME */
8✔
109
    bxstr_t *cldW = bxs_from_unicode(cld_u32);
8✔
110
    BFREE(cld_u32);
8✔
111

112
    dp->shape[W].name = W;
8✔
113
    dp->shape[W].height = 1;
8✔
114
    dp->shape[W].width = cldW->num_columns;
8✔
115
    dp->shape[W].elastic = 1;
8✔
116
    rc = genshape(dp->shape[W].width, dp->shape[W].height, &(dp->shape[W].chars), &(dp->shape[W].mbcs));
8✔
117
    if (rc) {
8!
118
        return rc;
×
119
    }
120
    bxs_free(dp->shape[W].mbcs[0]);
8✔
121
    dp->shape[W].mbcs[0] = cldW;
8✔
122
    strcpy(dp->shape[W].chars[0], cld);
8✔
123

124
    for (i = 0; i < NUM_SHAPES; ++i) {
136✔
125
        c = dp->shape + i;
128✔
126

127
        if (i == NNW || i == NNE || i == WNW || i == ENE || i == W
160✔
128
                || i == WSW || i == ESE || i == SSW || i == SSE) {
92✔
129
                    continue;
72✔
130
        }
131

132
        switch (i) {
56✔
133
            case NW:
8✔
134
            case SW:
135
                c->width = dp->shape[W].width;
16✔
136
                c->height = 1;
16✔
137
                c->elastic = 0;
16✔
138
                break;
16✔
139

140
            case NE:
8✔
141
            case SE:
142
                c->width = 1;
16✔
143
                c->height = 1;
16✔
144
                c->elastic = 0;
16✔
145
                break;
16✔
146

147
            case N:
12✔
148
            case S:
149
            case E:
150
                c->width = 1;
24✔
151
                c->height = 1;
24✔
152
                c->elastic = 1;
24✔
153
                break;
24✔
154

155
            default:
156
                fprintf(stderr, "%s: internal error\n", PROJECT);
×
157
                return 1;                /* never happens ;-) */
×
158
        }
159
        c->name = i;
56✔
160

161
        rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
56✔
162
        if (rc) {
56✔
163
            return rc;
×
164
        }
165
    }
28✔
166

167
    dp->maxshapeheight = 1;
8✔
168
    dp->minwidth = dp->shape[W].width + 2;
8✔
169
    dp->minheight = 3;
8✔
170

171
    return 0;
8✔
172
}
4✔
173

174

175

176
/**
177
 * Process command line options and store the result in the global `opt` struct. May exit the program.
178
 */
179
static void handle_command_line(int argc, char *argv[])
794✔
180
{
181
    #ifdef DEBUG
182
        fprintf (stderr, "Processing Command Line ...\n");
183
    #endif
184
    opt_t *parsed_opts = process_commandline(argc, argv);
794✔
185
    if (parsed_opts == NULL) {
794✔
186
        exit(EXIT_FAILURE);
20✔
187
    }
188
    if (parsed_opts->help) {
774✔
189
        usage_long(stdout);
2✔
190
        exit(EXIT_SUCCESS);
2✔
191
    }
192
    if (parsed_opts->version_requested) {
772!
193
        printf("%s version %s\n", PROJECT, VERSION);
×
194
        exit(EXIT_SUCCESS);
×
195
    }
196
    memcpy(&opt, parsed_opts, sizeof(opt_t));
772✔
197
    BFREE(parsed_opts);
772✔
198
}
772✔
199

200

201

202
/**
203
 * Parse config file(s), then reset design pointer. May exit the program.
204
 */
205
static void handle_config_parsing()
772✔
206
{
207
    bxstr_t *config_file = discover_config_file(0);
772✔
208
    if (config_file == NULL) {
772✔
209
        exit(EXIT_FAILURE);
6✔
210
    }
211
    if (opt.cld == NULL) {
766✔
212
        size_t r_num_designs = 0;
758✔
213
        designs = parse_config_files(config_file, &r_num_designs);
758✔
214
        if (designs == NULL) {
758✔
215
            exit(EXIT_FAILURE);
6✔
216
        }
217
        num_designs = (int) r_num_designs;
752✔
218
    }
376✔
219
    else {
220
        int rc = build_design(&designs, opt.cld);
8✔
221
        if (rc) {
8✔
222
            exit(EXIT_FAILURE);
×
223
        }
224
        num_designs = 1;
8✔
225
    }
226
    BFREE (opt.design);
760✔
227
    opt.design = designs;
760✔
228
}
760✔
229

230

231

232
/**
233
 * Adjust box size to command line specification.
234
 * Increase box width/height by width/height of empty sides in order to match appearance of box with the user's
235
 * expectations (if -s).
236
 */
237
static void apply_expected_size()
694✔
238
{
239
    if (opt.reqheight > (long) opt.design->minheight) {
694✔
240
        opt.design->minheight = opt.reqheight;
120✔
241
    }
60✔
242
    if (opt.reqwidth > (long) opt.design->minwidth) {
694✔
243
        opt.design->minwidth = opt.reqwidth;
120✔
244
    }
60✔
245
    if (opt.reqwidth) {
694✔
246
        if (empty_side(opt.design->shape, BRIG)) {
130✔
247
            opt.design->minwidth += opt.design->shape[SE].width;
4✔
248
        }
2✔
249
        if (empty_side(opt.design->shape, BLEF)) {
130✔
250
            opt.design->minwidth += opt.design->shape[NW].width;
4✔
251
        }
2✔
252
    }
65✔
253
    if (opt.reqheight) {
694✔
254
        if (empty_side(opt.design->shape, BTOP)) {
128!
255
            opt.design->minheight += opt.design->shape[NW].height;
×
256
        }
257
        if (empty_side(opt.design->shape, BBOT)) {
128!
258
            opt.design->minheight += opt.design->shape[SE].height;
×
259
        }
260
    }
64✔
261
}
694✔
262

263

264

265
/**
266
 * Read all input lines and store the result in the global `input` structure. May exit the program.
267
 */
268
static void handle_input()
840✔
269
{
270
    #ifdef DEBUG
271
        fprintf (stderr, "Reading all input ...\n");
272
    #endif
273
    input_t *raw_input = NULL;
840✔
274
    if (opt.mend != 0) {
840✔
275
        raw_input = read_all_input();
694✔
276
        if (raw_input == NULL) {
694!
277
            exit(EXIT_FAILURE);
×
278
        }
279
    }
347✔
280
    if (analyze_input(raw_input ? raw_input : &input)) {
840✔
281
        exit(EXIT_FAILURE);
×
282
    }
283
    if (raw_input) {
840✔
284
        memcpy(&input, raw_input, sizeof(input_t));
694✔
285
        BFREE(raw_input);
694✔
286
    }
347✔
287

288
    #ifdef DEBUG
289
        fprintf(stderr, "Effective encoding: %s\n", encoding);
290
        print_input_lines(NULL);
291
    #endif
292
    if (input.num_lines == 0) {
840✔
293
        exit(EXIT_SUCCESS);
×
294
    }
295
}
840✔
296

297

298

299
/**
300
 * Adjust box size to fit requested padding value.
301
 * Command line-specified box size takes precedence over padding.
302
 */
303
static void adjust_size_and_padding()
840✔
304
{
305
    for (int i = 0; i < NUM_SIDES; ++i) {
4,200✔
306
        if (opt.padding[i] > -1) {
3,360✔
307
            opt.design->padding[i] = opt.padding[i];
50✔
308
        }
25✔
309
    }
1,680✔
310

311
    size_t pad = opt.design->padding[BTOP] + opt.design->padding[BBOT];
840✔
312
    if (pad > 0) {
840✔
313
        pad += input.num_lines;
176✔
314
        pad += opt.design->shape[NW].height + opt.design->shape[SW].height;
176✔
315
        if (pad > opt.design->minheight) {
176✔
316
            if (opt.reqheight) {
168✔
317
                for (int i = 0; i < (int) (pad - opt.design->minheight); ++i) {
14✔
318
                    if (opt.design->padding[i % 2 ? BBOT : BTOP]) {
12✔
319
                        opt.design->padding[i % 2 ? BBOT : BTOP] -= 1;
10✔
320
                    } else if (opt.design->padding[i % 2 ? BTOP : BBOT]) {
7!
321
                        opt.design->padding[i % 2 ? BTOP : BBOT] -= 1;
×
322
                    } else {
323
                        break;
2✔
324
                    }
325
                }
5✔
326
            }
2✔
327
            else {
328
                opt.design->minheight = pad;
164✔
329
            }
330
        }
84✔
331
    }
88✔
332

333
    pad = opt.design->padding[BLEF] + opt.design->padding[BRIG];
840✔
334
    if (pad > 0) {
840✔
335
        pad += input.maxline;
728✔
336
        pad += opt.design->shape[NW].width + opt.design->shape[NE].width;
728✔
337
        if (pad > opt.design->minwidth) {
728✔
338
            if (opt.reqwidth) {
614✔
339
                for (int i = 0; i < (int) (pad - opt.design->minwidth); ++i) {
38✔
340
                    if (opt.design->padding[i % 2 ? BRIG : BLEF]) {
32✔
341
                        opt.design->padding[i % 2 ? BRIG : BLEF] -= 1;
26✔
342
                    } else if (opt.design->padding[i % 2 ? BLEF : BRIG]) {
19✔
343
                        opt.design->padding[i % 2 ? BLEF : BRIG] -= 1;
2!
344
                    } else {
1✔
345
                        break;
4✔
346
                    }
347
                }
14✔
348
            }
5✔
349
            else {
350
                opt.design->minwidth = pad;
604✔
351
            }
352
        }
307✔
353
    }
364✔
354
}
840✔
355

356

357

358
/**
359
 * Generate box. May exit the program.
360
 */
361
static void handle_generate_box()
518✔
362
{
363
    #ifdef DEBUG
364
        fprintf (stderr, "Generating Box ...\n");
365
    #endif
366
    sentry_t *thebox = (sentry_t *) calloc(NUM_SIDES, sizeof(sentry_t));
518✔
367
    if (thebox == NULL) {
518✔
368
        perror(PROJECT);
×
369
        exit(EXIT_FAILURE);
×
370
    }
371
    int rc = generate_box(thebox);
518✔
372
    if (rc) {
518!
373
        exit(EXIT_FAILURE);
×
374
    }
375
    output_box(thebox);
518✔
376
}
518✔
377

378

379

380
/**
381
 * Remove box. May exit the program.
382
 */
383
static void handle_remove_box()
321✔
384
{
385
    #ifdef DEBUG
386
        fprintf (stderr, "Removing Box ...\n");
387
    #endif
388
    if (opt.killblank == -1) {
321✔
389
        if (empty_side(opt.design->shape, BTOP) && empty_side(opt.design->shape, BBOT)) {
174✔
390
            opt.killblank = 0;
22✔
391
        } else {
11✔
392
            opt.killblank = 1;
152✔
393
        }
394
    }
87✔
395
    int rc = remove_box();
321✔
396
    if (rc) {
320✔
397
        exit(EXIT_FAILURE);
×
398
    }
399
    rc = apply_substitutions(&input, 1);
320✔
400
    if (rc) {
320!
401
        exit(EXIT_FAILURE);
×
402
    }
403
    output_input(opt.mend > 0);
320✔
404
}
320✔
405

406

407

408
#ifndef __MINGW32__
409
    /* These two functions are actually declared in term.h, but for some reason, that can't be included. */
410
    extern NCURSES_EXPORT(int) setupterm(NCURSES_CONST char *, int, int *);
411
    extern NCURSES_EXPORT(int) tigetnum(NCURSES_CONST char *);
412
#endif
413

414
static int terminal_has_colors()
770✔
415
{
416
    int result = 0;
770✔
417
    char *termtype = getenv("TERM");
770✔
418
    #ifdef __MINGW32__
419
        result = 1; /* On Windows, we always assume color capability. */
420
        UNUSED(termtype);
421
    #else
422
        if (termtype != NULL && setupterm(termtype, STDOUT_FILENO, NULL) == OK && tigetnum("colors") >= 8) {
770!
423
            result = 1;
770✔
424
        }
385✔
425
    #endif
426
    #if defined(DEBUG)
427
        #ifdef __MINGW32__
428
            int num_colors = 1;
429
        #else
430
            int num_colors = result ? tigetnum("colors") : 0;
431
        #endif
432
        fprintf(stderr, "Terminal \"%s\" %s colors (number of colors = %d).\n", termtype != NULL ? termtype : "(null)",
433
                result ? "has" : "does NOT have", num_colors);
434
    #endif
435
    return result;
770✔
436
}
437

438

439

440
static int check_color_support(int opt_color)
772✔
441
{
442
    int result = 0;
772✔
443
    if (opt_color == force_ansi_color) {
772✔
444
        result = 1;
2✔
445
    }
1✔
446
    else if (opt_color == color_from_terminal) {
770✔
447
        result = terminal_has_colors();
770✔
448
    }
385✔
449

450
    #if defined(DEBUG)
451
        fprintf(stderr, "%s: Color support %sabled\n", PROJECT, result ? "\x1b[92mEN\x1b[0m" : "DIS");
452
    #endif
453
    return result;
772✔
454
}
455

456

457

458
/*       _\|/_
459
         (o o)
460
 +----oOO-{_}-OOo------------------------------------------------------------+
461
 |                       P r o g r a m   S t a r t                           |
462
 +--------------------------------------------------------------------------*/
463

464
int main(int argc, char *argv[])
777✔
465
{
466
    int rc;                           /* general return code */
467
    int saved_designwidth;            /* opt.design->minwith backup, used for mending */
468
    int saved_designheight;           /* opt.design->minheight backup, used for mending */
469

470
    #ifdef DEBUG
471
        fprintf (stderr, "BOXES STARTING ...\n");
472
    #endif
473

474
    /* Temporarily set the system encoding, for proper output of --help text etc. */
475
    setlocale(LC_ALL, "");    /* switch from default "C" encoding to system encoding */
777✔
476
    encoding = locale_charset();
777✔
477

478
    handle_command_line(argc, argv);
777✔
479

480
    /* Store system character encoding */
481
    encoding = check_encoding(opt.encoding, locale_charset());
766✔
482
    #ifdef DEBUG
483
        fprintf (stderr, "Character Encoding = %s\n", encoding);
484
    #endif
485

486
    color_output_enabled = check_color_support(opt.color);
766✔
487

488
    handle_config_parsing();
766✔
489

490
    /* If "-l" option was given, list designs and exit. */
491
    if (opt.l) {
760✔
492
        rc = list_designs();
48✔
493
        exit(rc);
48✔
494
    }
495

496
    /* If "-q" option was given, print results of tag query and exit. */
497
    if (opt.query != NULL && opt.query[0] != NULL && !query_is_undoc()) {
712!
498
        rc = query_by_tag();
18✔
499
        exit(rc);
18✔
500
    }
501

502
    apply_expected_size();
694✔
503
    if (opt.indentmode) {
694✔
504
        opt.design->indentmode = opt.indentmode;
4✔
505
    }
2✔
506
    saved_designwidth = opt.design->minwidth;
694✔
507
    saved_designheight = opt.design->minheight;
694✔
508

509
    do {
347✔
510
        if (opt.mend == 1) {  /* Mending a box works in two phases: */
840✔
511
            opt.r = 0;        /* opt.mend == 2: remove box          */
146✔
512
        }
73✔
513
        --opt.mend;           /* opt.mend == 1: add it back         */
840✔
514
        opt.design->minwidth = saved_designwidth;
840✔
515
        opt.design->minheight = saved_designheight;
840✔
516

517
        handle_input();
840✔
518

519
        adjust_size_and_padding();
840✔
520

521
        if (opt.r) {
840✔
522
            handle_remove_box();
322✔
523
        }
161✔
524
        else {
525
            handle_generate_box();
518✔
526
        }
527
    } while (opt.mend > 0);
839✔
528

529
    return EXIT_SUCCESS;
693✔
530
}
531

532
/*EOF*/                                                  /* 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