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

ascii-boxes / boxes / 6518013212

14 Oct 2023 01:37PM UTC coverage: 81.211% (-0.4%) from 81.608%
6518013212

push

github

tsjensen
remove

2349 of 3210 branches covered (0.0%)

Branch coverage included in aggregate %.

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

3767 of 4321 relevant lines covered (87.18%)

7801.63 hits per line

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

71.56
/src/input.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
 * 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 "regulex.h"
30
#include "tools.h"
31
#include "unicode.h"
32

33

34

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

55

56

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

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

76
    for (size_t j = 0; j < lines_size; ++j) {
796✔
77
        if (lines[j].text->num_columns > 0) {
674✔
78
            nonblank = 1;
658✔
79
            size_t ispc = lines[j].text->indent;
658✔
80
            if ((int) ispc < res) {
658✔
81
                res = ispc;
143✔
82
            }
83
        }
84
    }
85

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

94

95

96
int apply_substitutions(input_t *result, const int mode)
122✔
97
{
98
    size_t anz_rules;
99
    reprule_t *rules;
100
    size_t j, k;
101

102
    if (opt.design == NULL) {
122!
103
        return 1;
×
104
    }
105

106
    if (mode == 0) {
122✔
107
        anz_rules = opt.design->anz_reprules;
114✔
108
        rules = opt.design->reprules;
114✔
109
    }
110
    else if (mode == 1) {
8!
111
        anz_rules = opt.design->anz_revrules;
8✔
112
        rules = opt.design->revrules;
8✔
113
    }
114
    else {
115
        fprintf(stderr, "%s: internal error\n", PROJECT);
×
116
        return 2;
×
117
    }
118

119
    /*
120
     *  Compile regular expressions
121
     */
122
    #ifdef REGEXP_DEBUG
123
        fprintf(stderr, "Compiling %d %s rule patterns\n", (int) anz_rules, mode ? "reversion" : "replacement");
124
    #endif
125
    errno = 0;
122✔
126
    opt.design->current_rule = rules;
122✔
127
    for (j = 0; j < anz_rules; ++j, ++(opt.design->current_rule)) {
227✔
128
        rules[j].prog = u32_compile_pattern(rules[j].search->memory);
105✔
129
        if (rules[j].prog == NULL) {
105!
130
            return 5;
×
131
        }
132
    }
133
    opt.design->current_rule = NULL;
122✔
134
    if (errno) {
122!
135
        return 3;
×
136
    }
137

138
    /*
139
     *  Apply regular expression substitutions to input lines
140
     */
141
    for (k = 0; k < result->num_lines; ++k) {
755✔
142
        opt.design->current_rule = rules;
633✔
143
        for (j = 0; j < anz_rules; ++j, ++(opt.design->current_rule)) {
1,207✔
144
            #ifdef REGEXP_DEBUG
145
                char *outtext = bxs_to_output(result->lines[k].text);
146
                char *outrepstr = bxs_to_output(rules[j].repstr);
147
                fprintf(stderr, "regex_replace(0x%p, \"%s\", \"%s\", %d, \'%c\') == ", rules[j].prog, outrepstr,
148
                    outtext, (int) result->lines[k].text->num_chars, rules[j].mode);
149
                BFREE(outtext);
150
                BFREE(outrepstr);
151
            #endif
152
            uint32_t *newtext = u32_regex_replace(rules[j].prog, rules[j].repstr->memory, result->lines[k].text->memory,
574✔
153
                    result->lines[k].text->num_chars, rules[j].mode == 'g');
574✔
154
            #ifdef REGEXP_DEBUG
155
                char *outnewtext = newtext ? u32_strconv_to_output(newtext) : strdup("NULL");
156
                fprintf(stderr, "\"%s\"\n", outnewtext);
157
                BFREE(outnewtext);
158
            #endif
159
            if (newtext == NULL) {
574!
160
                return 1;
×
161
            }
162

163
            bxs_free(result->lines[k].text);
574✔
164
            result->lines[k].text = bxs_from_unicode(newtext);
574✔
165

166
            analyze_line_ascii(result, result->lines + k);   /* update maxline value */
574✔
167

168
            #ifdef REGEXP_DEBUG
169
                char *outtext2 = bxs_to_output(result->lines[k].text);
170
                fprintf(stderr, "result->lines[%d] == {%d, \"%s\"}\n", (int) k, (int) result->lines[k].text->num_chars,
171
                    outtext2);
172
                BFREE(outtext2);
173
            #endif
174
        }
175
        opt.design->current_rule = NULL;
633✔
176
    }
177

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

193
    return 0;
122✔
194
}
195

196

197

198
static void trim_trailing_ws_carefully(uint32_t *mbtemp, size_t *len_chars)
666✔
199
{
200
    if (opt.r) {
666✔
201
        /* remove only trailing line breaks, but keep the space */
202
        if (is_char_at(mbtemp, *len_chars - 1, char_newline)) {
69!
203
            set_char_at(mbtemp, *len_chars - 1, char_nul);
69✔
204
            --(*len_chars);
69✔
205
        }
206
        if (is_char_at(mbtemp, *len_chars - 1, char_cr)) {
69!
207
            set_char_at(mbtemp, *len_chars - 1, char_nul);
×
208
            --(*len_chars);
×
209
        }
210
    }
211
    else {
212
        /* remove all trailing whitespace, including unicode whitespace */
213
        btrim32(mbtemp, len_chars);
597✔
214
    }
215
}
666✔
216

217

218

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

224
    input_t *result = (input_t *) calloc(1, sizeof(input_t));
121✔
225
    result->indent = LINE_MAX_BYTES;
121✔
226

227
    while (fgets(buf, LINE_MAX_BYTES + 2, opt.infile)) {
787✔
228
        if (result->num_lines % 100 == 0) {
666✔
229
            input_size += 100;
123✔
230
            line_t *tmp = (line_t *) realloc(result->lines, input_size * sizeof(line_t));
123✔
231
            if (tmp == NULL) {
123!
232
                perror(PROJECT);
×
233
                BFREE(result->lines);
×
234
                return NULL;
×
235
            }
236
            result->lines = tmp;
123✔
237
        }
238

239
        memset(result->lines + result->num_lines, 0, sizeof(line_t));
666✔
240

241
        uint32_t *mbtemp = u32_strconv_from_input(buf);
666✔
242
        size_t len_chars = u32_strlen(mbtemp);
666✔
243
        result->final_newline = has_linebreak(mbtemp, len_chars);
666✔
244
        trim_trailing_ws_carefully(mbtemp, &len_chars);
666✔
245

246
        /*
247
         * Expand tabs
248
         */
249
        if (len_chars > 0) {
666✔
250
            uint32_t *temp = NULL;
650✔
251
            len_chars = expand_tabs_into(mbtemp, opt.tabstop, &temp, &(result->lines[result->num_lines].tabpos),
1,300✔
252
                    &(result->lines[result->num_lines].tabpos_len));
650✔
253
            if (len_chars == 0) {
650!
254
                perror(PROJECT);
×
255
                BFREE(result->lines);
×
256
                return NULL;
×
257
            }
258
            result->lines[result->num_lines].text = bxs_from_unicode(temp);
650✔
259
            BFREE(temp);
650!
260
        }
261
        else {
262
            result->lines[result->num_lines].text = bxs_new_empty_string();
16✔
263
        }
264

265
        BFREE(mbtemp);
666!
266
        ++result->num_lines;
666✔
267
    }
268

269
    if (ferror(stdin)) {
121!
270
        perror(PROJECT);
×
271
        BFREE(result->lines);
×
272
        return NULL;
×
273
    }
274
    return result;
121✔
275
}
276

277

278

279
int analyze_input(input_t *result)
122✔
280
{
281
    result->indent = LINE_MAX_BYTES;
122✔
282
    result->maxline = 0;
122✔
283

284
    /*
285
     * Build ASCII equivalent of the multi-byte string, update line stats
286
     */
287
    for (size_t i = 0; i < result->num_lines; ++i) {
796✔
288
        analyze_line_ascii(result, result->lines + i);
674✔
289
    }
290

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

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

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

337
    /*
338
     *  Apply regular expression substitutions
339
     */
340
    if (opt.r == 0) {
122✔
341
        if (apply_substitutions(result, 0) != 0) {
114!
342
            return 1;
×
343
        }
344
    }
345

346
    return 0;
122✔
347
}
348

349

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

© 2025 Coveralls, Inc