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

ascii-boxes / boxes / 11084497071

28 Sep 2024 01:00PM UTC coverage: 87.312% (-1.6%) from 88.939%
11084497071

push

github

tsjensen
Remove unused function array_contains() from 'tools' module

3132 of 3809 branches covered (82.23%)

Branch coverage included in aggregate %.

5091 of 5609 relevant lines covered (90.76%)

175335.5 hits per line

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

83.62
/src/input.c
1
/*
2
 * boxes - Command line filter to draw/remove ASCII boxes around text
3
 * Copyright (c) 1999-2024 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
 * Read and analyze input text.
18
 */
19

20
#include "config.h"
21

22
#include <errno.h>
23
#include <string.h>
24
#include <unistr.h>
25
#include <unitypes.h>
26

27
#include "boxes.h"
28
#include "input.h"
29
#include "logging.h"
30
#include "regulex.h"
31
#include "tools.h"
32
#include "unicode.h"
33

34

35

36
/**
37
 * Determine if the given line of raw text is ended by a line break.
38
 * @param s the string to check
39
 * @param len length of s in characters
40
 * @returns != 0 if line break found;
41
 *          == 0 if line break not found
42
 */
43
static int has_linebreak(const uint32_t *s, const int len)
5,270✔
44
{
45
    int result = 0;
5,270✔
46
    if (s != NULL && len > 0) {
5,270!
47
        ucs4_t the_last = s[len - 1];
5,270✔
48
        result = u32_cmp(&char_cr, &the_last, 1) == 0 || u32_cmp(&char_newline, &the_last, 1) == 0;
5,270!
49
        log_debug(__FILE__, MAIN, "has_linebreak: (%#010x) %d\n", (int) the_last, result);
5,270✔
50
    }
2,635✔
51
    return result;
5,270✔
52
}
53

54

55

56
/**
57
 * Determine indentation of given lines in spaces. Lines are assumed to be free of trailing whitespace.
58
 * @param lines the lines to examine
59
 * @param lines_size number of lines to examine
60
 * @returns >= 0: indentation in spaces; < 0: error
61
 */
62
static int get_indent(const line_t *lines, const size_t lines_size)
848✔
63
{
64
    int res = LINE_MAX_BYTES; /* result */
848✔
65
    int nonblank = 0;         /* true if one non-blank line found */
848✔
66

67
    if (lines == NULL) {
848✔
68
        bx_fprintf(stderr, "%s: internal error\n", PROJECT);
×
69
        return -1;
×
70
    }
71
    if (lines_size == 0) {
848✔
72
        return 0;
×
73
    }
74

75
    for (size_t j = 0; j < lines_size; ++j) {
6,894✔
76
        if (lines[j].text->num_columns > 0) {
6,046✔
77
            nonblank = 1;
6,008✔
78
            size_t ispc = lines[j].text->indent;
6,008✔
79
            if ((int) ispc < res) {
6,008✔
80
                res = ispc;
1,260✔
81
            }
630✔
82
        }
3,004✔
83
    }
3,023✔
84

85
    if (nonblank) {
848✔
86
        return res; /* success */
848✔
87
    }
88
    else {
89
        return 0; /* success, but only blank lines */
×
90
    }
91
}
424✔
92

93

94

95
int apply_substitutions(input_t *result, const int mode)
846✔
96
{
97
    size_t num_rules;
98
    reprule_t *rules;
99
    size_t j, k;
100

101
    if (opt.design == NULL) {
846✔
102
        return 1;
×
103
    }
104

105
    if (mode == 0) {
846✔
106
        num_rules = opt.design->num_reprules;
526✔
107
        rules = opt.design->reprules;
526✔
108
    }
263✔
109
    else if (mode == 1) {
320!
110
        num_rules = opt.design->num_revrules;
320✔
111
        rules = opt.design->revrules;
320✔
112
    }
160✔
113
    else {
114
        bx_fprintf(stderr, "%s: internal error\n", PROJECT);
×
115
        return 2;
×
116
    }
117

118
    /*
119
     *  Compile regular expressions
120
     */
121
    log_debug(__FILE__, REGEXP, "Compiling %d %s rule patterns\n", (int) num_rules, mode ? "reversion" : "replacement");
846✔
122
    errno = 0;
846✔
123
    opt.design->current_rule = rules;
846✔
124
    for (j = 0; j < num_rules; ++j, ++(opt.design->current_rule)) {
1,172✔
125
        rules[j].prog = u32_compile_pattern(rules[j].search->memory);
326✔
126
        if (rules[j].prog == NULL) {
326✔
127
            return 5;
×
128
        }
129
    }
163✔
130
    opt.design->current_rule = NULL;
846✔
131
    if (errno) {
846!
132
        return 3;
×
133
    }
134

135
    /*
136
     *  Apply regular expression substitutions to input lines
137
     */
138
    for (k = 0; k < result->num_lines; ++k) {
5,128✔
139
        opt.design->current_rule = rules;
4,282✔
140
        for (j = 0; j < num_rules; ++j, ++(opt.design->current_rule)) {
6,002✔
141
            if (is_debug_logging(REGEXP)) {
1,720✔
142
                char *outtext = bxs_to_output(result->lines[k].text);
2✔
143
                char *outrepstr = bxs_to_output(rules[j].repstr);
2✔
144
                log_debug(__FILE__, REGEXP, "regex_replace(0x%p, \"%s\", \"%s\", %d, \'%c\') == ", rules[j].prog,
3✔
145
                    outrepstr, outtext, (int) result->lines[k].text->num_chars, rules[j].mode);
2✔
146
                BFREE(outtext);
2✔
147
                BFREE(outrepstr);
2✔
148
            }
1✔
149
            uint32_t *newtext = u32_regex_replace(rules[j].prog, rules[j].repstr->memory, result->lines[k].text->memory,
2,580✔
150
                    result->lines[k].text->num_chars, rules[j].mode == 'g');
1,720✔
151
            if (is_debug_logging(REGEXP)) {
1,720✔
152
                char *outnewtext = newtext ? u32_strconv_to_output(newtext) : strdup("NULL");
2!
153
                log_debug_cont(REGEXP, "\"%s\"\n", outnewtext);
2✔
154
                BFREE(outnewtext);
2✔
155
            }
1✔
156
            if (newtext == NULL) {
1,720✔
157
                return 1;
×
158
            }
159

160
            bxs_free(result->lines[k].text);
1,720✔
161
            result->lines[k].text = bxs_from_unicode(newtext);
1,720✔
162

163
            analyze_line_ascii(result, result->lines + k);   /* update maxline value */
1,720✔
164

165
            if (is_debug_logging(REGEXP)) {
1,720✔
166
                char *outtext2 = bxs_to_output(result->lines[k].text);
2✔
167
                log_debug(__FILE__, REGEXP, "result->lines[%d] == {%d, \"%s\"}\n",
2✔
168
                    (int) k, (int) result->lines[k].text->num_chars, outtext2);
2✔
169
                BFREE(outtext2);
2✔
170
            }
1✔
171
        }
860✔
172
        opt.design->current_rule = NULL;
4,282✔
173
    }
2,141✔
174

175
    /*
176
     *  If text indentation was part of the lines processed, indentation
177
     *  may now be different -> recalculate result->indent.
178
     */
179
    if (opt.design->indentmode == 't') {
846✔
180
        int rc;
181
        rc = get_indent(result->lines, result->num_lines);
×
182
        if (rc >= 0) {
×
183
            result->indent = (size_t) rc;
×
184
        }
185
        else {
186
            return 4;
×
187
        }
188
    }
189

190
    return 0;
846✔
191
}
423✔
192

193

194

195
static void trim_trailing_ws_carefully(uint32_t *mbtemp, size_t *len_chars)
5,270✔
196
{
197
    if (opt.r) {
5,270✔
198
        /* remove only trailing line breaks, but keep the space */
199
        if (is_char_at(mbtemp, *len_chars - 1, char_newline)) {
3,346✔
200
            set_char_at(mbtemp, *len_chars - 1, char_nul);
3,346✔
201
            --(*len_chars);
3,346✔
202
        }
1,673✔
203
        if (is_char_at(mbtemp, *len_chars - 1, char_cr)) {
3,346✔
204
            set_char_at(mbtemp, *len_chars - 1, char_nul);
×
205
            --(*len_chars);
×
206
        }
207
    }
1,673✔
208
    else {
209
        /* remove all trailing whitespace, including unicode whitespace */
210
        btrim32(mbtemp, len_chars);
1,924✔
211
    }
212
}
5,270✔
213

214

215

216
input_t *read_all_input()
702✔
217
{
218
    char buf[LINE_MAX_BYTES + 3];      /* static input buffer incl. newline + zero terminator */
219
    size_t input_size = 0;             /* number of elements allocated */
702✔
220

221
    input_t *result = (input_t *) calloc(1, sizeof(input_t));
702✔
222
    result->indent = LINE_MAX_BYTES;
702✔
223

224
    while (fgets(buf, LINE_MAX_BYTES + 2, opt.infile)) {
5,972✔
225
        if (result->num_lines % 100 == 0) {
5,270✔
226
            input_size += 100;
706✔
227
            line_t *tmp = (line_t *) realloc(result->lines, input_size * sizeof(line_t));
706✔
228
            if (tmp == NULL) {
706✔
229
                perror(PROJECT);
×
230
                BFREE(result->lines);
×
231
                return NULL;
×
232
            }
233
            result->lines = tmp;
706✔
234
        }
353✔
235

236
        memset(result->lines + result->num_lines, 0, sizeof(line_t));
5,270✔
237

238
        uint32_t *mbtemp = u32_strconv_from_input(buf);
5,270✔
239
        size_t len_chars = u32_strlen(mbtemp);
5,270✔
240
        result->final_newline = has_linebreak(mbtemp, len_chars);
5,270✔
241
        trim_trailing_ws_carefully(mbtemp, &len_chars);
5,270✔
242

243
        /*
244
         * Expand tabs
245
         */
246
        if (len_chars > 0) {
5,270✔
247
            uint32_t *temp = NULL;
5,232✔
248
            len_chars = expand_tabs_into(mbtemp, opt.tabstop, &temp, &(result->lines[result->num_lines].tabpos),
10,464✔
249
                    &(result->lines[result->num_lines].tabpos_len));
5,232✔
250
            if (len_chars == 0) {
5,232✔
251
                perror(PROJECT);
×
252
                BFREE(result->lines);
×
253
                return NULL;
×
254
            }
255
            result->lines[result->num_lines].text = bxs_from_unicode(temp);
5,232✔
256
            BFREE(temp);
5,232✔
257
        }
2,616✔
258
        else {
259
            result->lines[result->num_lines].text = bxs_new_empty_string();
38✔
260
        }
261

262
        BFREE(mbtemp);
5,270!
263
        ++result->num_lines;
5,270✔
264
    }
265

266
    if (ferror(stdin)) {
702✔
267
        perror(PROJECT);
×
268
        BFREE(result->lines);
×
269
        return NULL;
×
270
    }
271
    return result;
702✔
272
}
351✔
273

274

275

276
int analyze_input(input_t *result)
848✔
277
{
278
    result->indent = LINE_MAX_BYTES;
848✔
279
    result->maxline = 0;
848✔
280

281
    /*
282
     * Build ASCII equivalent of the multi-byte string, update line stats
283
     */
284
    for (size_t i = 0; i < result->num_lines; ++i) {
6,894✔
285
        analyze_line_ascii(result, result->lines + i);
6,046✔
286
    }
3,023✔
287

288
    /*
289
     *  Exit if there was no input at all
290
     */
291
    if (result->lines == NULL || result->lines[0].text == NULL) {
848!
292
        return 0;
×
293
    }
294

295
    /*
296
     *  Compute indentation
297
     */
298
    int rc = get_indent(result->lines, result->num_lines);
848✔
299
    if (rc >= 0) {
848!
300
        result->indent = (size_t) rc;
848✔
301
    }
424✔
302
    else {
303
        return 1;
×
304
    }
305

306
    /*
307
     *  Remove indentation, unless we want to preserve it (when removing
308
     *  a box or if the user wants to retain it inside the box)
309
     */
310
    if (opt.design->indentmode != 't' && opt.r == 0) {
848!
311
        for (size_t i = 0; i < result->num_lines; ++i) {
3,226✔
312
            if (result->lines[i].text->num_columns >= result->indent) {
2,700✔
313
                /*
314
                 * We should really remove *columns* rather than *characters*, but since the removed characters are
315
                 * spaces (indentation), and there are no double-wide spaces in Unicode, both actions are equivalent.
316
                 */
317
                bxstr_t *unindented = bxs_cut_front(result->lines[i].text, result->indent);
2,700✔
318
                bxs_free(result->lines[i].text);
2,700✔
319
                result->lines[i].text = unindented;
2,700✔
320
            }
1,350✔
321
            if (is_debug_logging(MAIN)) {
2,700✔
322
                char *outtext = bxs_to_output(result->lines[i].text);
6✔
323
                log_debug(__FILE__, MAIN, "%2d: text = \"%s\" (%d chars, %d visible, %d invisible, %d columns)\n",
6✔
324
                    (int) i, outtext,
3✔
325
                    (int) result->lines[i].text->num_chars, (int) result->lines[i].text->num_chars_visible,
6✔
326
                    (int) result->lines[i].text->num_chars_invisible, (int) result->lines[i].text->num_columns);
6✔
327
                log_debug(__FILE__, MAIN, "    ascii = \"%s\"\n", result->lines[i].text->ascii);
6✔
328
                BFREE(outtext);
6!
329
            }
3✔
330
        }
1,350✔
331
        result->maxline -= result->indent;
526✔
332
    }
263✔
333

334
    /*
335
     *  Apply regular expression substitutions
336
     */
337
    if (opt.r == 0) {
848✔
338
        if (apply_substitutions(result, 0) != 0) {
526!
339
            return 1;
×
340
        }
341
    }
263✔
342

343
    return 0;
848✔
344
}
424✔
345

346

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