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

ascii-boxes / boxes / 25991660202

17 May 2026 01:04PM UTC coverage: 82.822%. Remained the same
25991660202

push

github

tsjensen
Fix a Heisenbug in u32_insert_space_at() in unicode.c

2776 of 3695 branches covered (75.13%)

Branch coverage included in aggregate %.

5 of 7 new or added lines in 1 file covered. (71.43%)

297 existing lines in 19 files now uncovered.

4345 of 4903 relevant lines covered (88.62%)

98937.49 hits per line

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

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

18
/*
19
 * Yacc parser for boxes configuration files
20
 */
21

22
#include "config.h"
23
#include "boxes.h"
24
#include "bxstring.h"
25

26

27
/** all the arguments which we pass to the bison parser */
28
typedef struct {
29
    /** bison will store the parsed designs here, also allocating memory as required */
30
    design_t *designs;
31

32
    /** the size of `*designs` */
33
    size_t num_designs;
34

35
    /** index into `*designs` */
36
    int design_idx;
37

38
    /** Box designs already parsed from child config files, if any. Else NULL */
39
    design_t *child_configs;
40

41
    /** the size of `*child_configs` */
42
    size_t num_child_configs;
43

44
    /** the path to the config file we are parsing */
45
    bxstr_t *config_file;
46

47
    int num_mandatory;
48

49
    int time_for_se_check;
50

51
    /** number of user-specified shapes */
52
    int num_shapespec;
53

54
    /** used to limit "skipping" msgs */
55
    int skipping;
56

57
    /** true if we're skipping designs, but no error */
58
    int speeding;
59

60
    /** names of config files specified via "parent" */
61
    bxstr_t **parent_configs;
62

63
    /** number of parent config files (size of parent_configs array) */
64
    size_t num_parent_configs;
65

66
    /** the flex scanner state, which is explicitly passed to reentrant bison */
67
    void *lexer_state;
68
} pass_to_bison;
69

70
}
71

72
%code {
73

74
#include "config.h"
75
#include <stdio.h>
76
#include <stdlib.h>
77
#include <unictype.h>
78

79
#include "logging.h"
80
#include "shape.h"
81
#include "tools.h"
82
#include "parsing.h"
83
#include "parser.h"
84
#include "lex.yy.h"
85
#include "parsecode.h"
86
#include "unicode.h"
87

88

89
/** required for bison-flex bridge */
90
#define scanner bison_args->lexer_state
91

92
/** invoke a parsecode action and react to its return code */
93
#define invoke_action(action) {   \
94
    int rc = (action);            \
95
    if (rc == RC_ERROR) {         \
96
        YYERROR;                  \
97
    } else if (rc == RC_ABORT) {  \
98
        YYABORT;                  \
99
    } else if (rc == RC_ACCEPT) { \
100
        YYACCEPT;                 \
101
    }                             \
102
}
103

104
}
105

106

107
/*\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\
108
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
109
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
110
|  |                                                                                            |  |
111
|  |                              B i s o n  D e f i n i t i o n s                              |  |
112
|  |                                                                                            |  |
113
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
114
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|_*/
115

116
%define api.pure true
117
%lex-param {void *scanner}
118
%parse-param {pass_to_bison *bison_args}
119

120
%union {
121
    bxstr_t *s;
122
    char *ascii;
123
    char c;
124
    shape_t shape;
125
    sentry_t sentry;
126
    int num;
127
}
128

129
%token YPARENT YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
130
%token YREPLACE YREVERSE YTO YWITH YCHGDEL YTAGS
131
%token <ascii> KEYWORD
132
%token <s> BXWORD
133
%token <ascii> ASCII_ID
134
%token <s> STRING
135
%token <s> FILENAME
136
%token <shape> SHAPE
137
%token <num> YNUMBER
138
%token <c> YRXPFLAG
139
%token <s> YDELIMSPEC
140

141
%type <sentry> shape_def
142
%type <sentry> shape_lines
143
%type <c> rflag
144

145
%start first_rule
146

147

148
%%
149

150
/*\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\
151
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
152
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
153
|  |                                                                                            |  |
154
|  |                             G r a m m a r   &   A c t i o n s                              |  |
155
|  |                                                                                            |  |
156
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
157
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|_*/
158

159

160
first_rule:
161
    {
162
        invoke_action(action_init_parser(bison_args));
397!
163
    }
164

165
config_file
166
    {
167
        /*
168
         *  Clean up parser data structures
169
         */
170
        design_t *tmp;
171

172
        if (bison_args->design_idx == 0) {
48✔
173
            BFREE (bison_args->designs);
8!
174
            bison_args->num_designs = 0;
8✔
175
            if (!opt.design_choice_by_user && bison_args->num_parent_configs == 0) {
8✔
176
                fprintf(stderr, "%s: no valid data in config file -- %s\n", PROJECT,
3✔
177
                        bxs_to_output(bison_args->config_file));
178
                YYABORT;
3✔
179
            }
180
            YYACCEPT;
5✔
181
        }
182

183
        --(bison_args->design_idx);
40✔
184
        bison_args->num_designs = bison_args->design_idx + 1;
40✔
185
        tmp = (design_t *) realloc (bison_args->designs, (bison_args->num_designs) * sizeof(design_t));
40✔
186
        if (!tmp) {
40!
187
            perror (PROJECT);
×
UNCOV
188
            YYABORT;
×
189
        }
190
        bison_args->designs = tmp;
40✔
191
    }
192

193
parent_def: YPARENT FILENAME
194
    {
195
        invoke_action(action_parent_config(bison_args, $2));
15!
196
    }
197

198
config_file: config_file design_or_error | design_or_error | config_file parent_def | parent_def ;
199

200

201
design_or_error: design | error
202
    {
203
        /* reset alias list, as those are collected even when speedmode is on */
204
        log_debug(__FILE__, PARSER, " Parser: Discarding token [skipping=%s, speeding=%s]\n",
1,663,248✔
205
                bison_args->skipping ? "true" : "false", bison_args->speeding ? "true" : "false");
1,663,248✔
206
        if (curdes.aliases[0] != NULL) {
831,624✔
207
            BFREE(curdes.aliases);
254!
208
            curdes.aliases = (char **) calloc(1, sizeof(char *));
254✔
209
        }
210
        if (!bison_args->speeding && !bison_args->skipping) {
831,624✔
211
            recover(bison_args);
9✔
212
            yyerror(bison_args, "skipping to next design");
9✔
213
            bison_args->skipping = 1;
9✔
214
        }
215
    }
216

217

218
alias: ASCII_ID
219
    {
220
        invoke_action(action_add_alias(bison_args, $1));
587!
221
    }
222

223
alias_list: alias | alias_list ',' alias;
224

225
design_id: ASCII_ID | ASCII_ID ',' alias_list
226

227
| BXWORD
228
    {
229
        yyerror(bison_args, "box design name must consist of printable standard ASCII characters.");
1✔
230
        YYERROR;
1✔
231
    }
232
;
233

234
design: YBOX design_id
235
    {
236
        invoke_action(action_start_parsing_design(bison_args, $<ascii>2));
9,056!
237
    }
238
layout YEND ASCII_ID
239
    {
240
        invoke_action(action_add_design(bison_args, $<ascii>2, $6));
862!
241
    }
242
;
243

244

245
layout: layout entry | layout block | entry | block ;
246

247

248
tag_entry: STRING
249
    {
250
        tag_record(bison_args, $1);    /* discard return code (we print warnings, but tolerate the problem) */
2,197✔
251
    }
252

253
tag_list: tag_entry | tag_list ',' tag_entry;
254

255

256
entry: KEYWORD STRING
257
    {
258
        invoke_action(action_record_keyword(bison_args, $1, $2));
1,477!
259
    }
260

261
| YPARENT FILENAME
262
    {
263
        /*
264
         * Called when PARENT appears as a key inside a box design. That's a user mistake, but not an error.
265
         */
266
        bxstr_t *filename = $2;
×
267
        if (filename->memory[0] != filename->memory[filename->num_chars - 1] || uc_is_alnum(filename->memory[0])) {
×
268
            yyerror(bison_args, "string expected");
×
UNCOV
269
            YYERROR;
×
270
        }
271
        else if (is_debug_logging(PARSER)) {
×
272
            char *out_filename = bxs_to_output(filename);
×
273
            log_debug(__FILE__, PARSER, " Parser: Discarding entry [%s = %s].\n", "parent", out_filename);
×
UNCOV
274
            BFREE(out_filename);
×
275
        }
276
    }
277

278
| YCHGDEL YDELIMSPEC
279
    {
280
        /* string delimiters were changed - this is a lexer thing. ignore here. */
281
    }
282

283
| YTAGS '(' tag_list ')' | YTAGS tag_entry
284

285
| BXWORD STRING | ASCII_ID STRING
286
    {
287
        if (is_debug_logging(PARSER)) {
5!
288
            char *out_string = bxs_to_output($2);
×
289
            log_debug(__FILE__, PARSER, " Parser: Discarding entry [%s = %s].\n", $1, out_string);
×
UNCOV
290
            BFREE(out_string);
×
291
        }
292
    }
293
;
294

295

296
block: YSAMPLE STRING YENDSAMPLE
297
    {
298
        invoke_action(action_sample_block(bison_args, $2));
865!
299
    }
300

301
| YSHAPES  '{' slist  '}'
302
    {
303
        invoke_action(action_finalize_shapes(bison_args));
864!
304
    }
305

306
| YELASTIC '(' elist ')'
307
    {
308
        ++(bison_args->num_mandatory);
861✔
309
        if (++(bison_args->time_for_se_check) > 1) {
861!
310
            if (perform_se_check(bison_args) != 0) {
861!
UNCOV
311
                YYERROR;
×
312
            }
313
        }
314
    }
315

316
| YREPLACE rflag STRING YWITH STRING
317
    {
318
        invoke_action(action_add_regex_rule(bison_args, "rep", &curdes.reprules, &curdes.num_reprules, $3, $5, $2));
215!
319
    }
320

321
| YREVERSE rflag STRING YTO STRING
322
    {
323
        invoke_action(action_add_regex_rule(bison_args, "rev", &curdes.revrules, &curdes.num_revrules, $3, $5, $2));
263!
324
    }
325

326
| YPADDING '{' wlist '}'
327
    {
328
        log_debug(__FILE__, PARSER, "Padding set to (l%d o%d r%d u%d)\n",
741✔
329
                curdes.padding[BLEF], curdes.padding[BTOP], curdes.padding[BRIG], curdes.padding[BBOT]);
741✔
330
    }
331
;
332

333

334
rflag: YRXPFLAG
335
    {
UNCOV
336
        $$ = $1;
×
337
    }
338
| %empty
339
    {
340
        $$ = 'g';
478✔
341
    }
342
;
343

344

345
elist: elist ',' elist_entry | elist_entry;
346

347

348
elist_entry: SHAPE
349
    {
350
        log_debug(__FILE__, PARSER, "Marked \'%s\' shape as elastic\n", shape_name[(int) $1]);
3,057✔
351
        curdes.shape[$1].elastic = 1;
3,057✔
352
    }
353
;
354

355

356
slist: slist slist_entry | slist_entry;
357

358

359
slist_entry: SHAPE shape_def
360
    {
361
        log_debug(__FILE__, PARSER, "Adding shape spec for \'%s\' (width %d height %d)\n",
6,644✔
362
                shape_name[$1], (int) $2.width, (int) $2.height);
6,644✔
363

364
        if (isempty (curdes.shape + $1)) {
6,644!
365
            curdes.shape[$1] = $2;
6,644✔
366
            if (!isdeepempty(&($2))) {
6,644✔
367
                ++(bison_args->num_shapespec);
6,478✔
368
            }
369
        }
370
        else {
371
            yyerror(bison_args, "duplicate specification for %s shape", shape_name[$1]);
×
UNCOV
372
            YYERROR;
×
373
        }
374
    }
375
;
376

377

378
shape_def: '(' shape_lines ')'
379
    {
380
        if ($2.width == 0 || $2.height == 0) {
6,626!
381
            yyerror(bison_args, "minimum shape dimension is 1x1 - clearing");
×
UNCOV
382
            freeshape (&($2));
×
383
        }
384
        $$ = $2;
6,626✔
385
    }
386

387
| '(' ')'
388
    {
389
        $$ = SENTRY_INITIALIZER;
18✔
390
    }
391
;
392

393

394
shape_lines: shape_lines ',' STRING
395
    {
396
        sentry_t rval = $1;
10,326✔
397
        invoke_action(action_add_shape_line(bison_args, &rval, $3));
10,326!
398
        $$ = rval;
10,326✔
399
    }
400

401
| STRING
402
    {
403
        sentry_t rval;
404
        invoke_action(action_first_shape_line(bison_args, $1, &rval));
6,626!
405
        $$ = rval;
6,626✔
406
    }
407
;
408

409

410
wlist: wlist wlist_entry | wlist_entry;
411

412
wlist_entry: ASCII_ID YNUMBER
413
    {
414
        invoke_action(action_padding_entry(bison_args, $1, $2));
931!
415
    }
416
;
417

418

419
%%
420

421

422
/* vim: set sw=4 cindent: */
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