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

ascii-boxes / boxes / 11001727581

23 Sep 2024 08:16PM UTC coverage: 82.798% (-0.3%) from 83.099%
11001727581

push

github

tsjensen
cmdline

2958 of 3811 branches covered (77.62%)

Branch coverage included in aggregate %.

0 of 22 new or added lines in 1 file covered. (0.0%)

6 existing lines in 1 file now uncovered.

4796 of 5554 relevant lines covered (86.35%)

175921.95 hits per line

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

74.64
/src/cmdline.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
 * Processing of command line options.
18
 */
19

20
#include "config.h"
21
#include <errno.h>
22
#include <getopt.h>
23
#include <stdio.h>
24
#include <string.h>
25
#include <strings.h>
26
#include <uniconv.h>
27
#include <unistd.h>
28

29
#ifdef __MINGW32__
30
#include <fcntl.h>  /* _O_BINARY */
31
#include <io.h>     /* _setmode() */
32
#endif
33

34
#include "boxes.h"
35
#include "bxstring.h"
36
#include "discovery.h"
37
#include "logging.h"
38
#include "query.h"
39
#include "tools.h"
40
#include "cmdline.h"
41

42

43
extern char *optarg;   /* for getopt() */
44
extern int optind;     /* for getopt() */
45

46

47
/* default tab stop distance (part of -t) */
48
#define DEF_TABSTOP 8
49

50
/* max. allowed tab stop distance */
51
#define MAX_TABSTOP 16
52

53

54
/* System default line terminator.
55
 * Used only for display in usage info. The real default is always "\n", with stdout in text mode. */
56
#ifdef __MINGW32__
57
    #define EOL_DEFAULT "\r\n"
58
#else
59
    #define EOL_DEFAULT "\n"
60
#endif
61

62
#define EXTRA_UNDOC "(undoc)"
63
#define EXTRA_DEBUG "debug"
64

65

66

67
/**
68
 * Print abbreviated usage information on stream `st`.
69
 * @param st the stream to print to
70
 */
71
static void usage_short(FILE *st)
6✔
72
{
73
    bx_fprintf(st, "Usage: %s [options] [infile [outfile]]\n", PROJECT);
6✔
74
    bx_fprintf(st, "Try `%s -h' for more information.\n", PROJECT);
6✔
75
}
6✔
76

77

78

79
/**
80
 * Print usage information on stream `st`, including a header text.
81
 * @param st the stream to print to
82
 */
83
void usage_long(FILE *st)
2✔
84
{
85
    bxstr_t *config_file = discover_config_file(0);
2✔
86

87
    fprintf(st, "%s - draws any kind of box around your text (or removes it)\n", PROJECT);
2✔
88
    fprintf(st, "        Website: https://boxes.thomasjensen.com/\n");
2✔
89
    fprintf(st, "Usage:  %s [options] [infile [outfile]]\n", PROJECT);
2✔
90
    fprintf(st, "  -a, --align <fmt>     Alignment/positioning of text inside box [default: hlvt]\n");
2✔
91
    fprintf(st, "  -c, --create <str>    Use single shape box design where str is the W shape\n");
2✔
92
    fprintf(st, "      --color           Force output of ANSI sequences if present\n");
2✔
93
    fprintf(st, "      --no-color        Force monochrome output (no ANSI sequences)\n");
2✔
94
    fprintf(st, "  -d, --design <name>   Box design [default: first one in file]\n");
2✔
95
    fprintf(st, "  -e, --eol <eol>       Override line break type (experimental) [default: %s]\n",
2✔
96
                                         strcmp(EOL_DEFAULT, "\r\n") == 0 ? "CRLF" : "LF");
97
    fprintf(st, "  -f, --config <file>   Configuration file [default: %s]\n",
4!
98
                                         config_file != NULL ? bxs_to_output(config_file) : "none");
2!
99
    fprintf(st, "  -h, --help            Print usage information\n");
2✔
100
    fprintf(st, "  -i, --indent <mode>   Indentation mode [default: box]\n");
2✔
101
    fprintf(st, "  -k <bool>             Leading/trailing blank line retention on removal\n");
2✔
102
    fprintf(st, "      --kill-blank      Kill leading/trailing blank lines on removal (like -k true)\n");
2✔
103
    fprintf(st, "      --no-kill-blank   Retain leading/trailing blank lines on removal (like -k false)\n");
2✔
104
    fprintf(st, "  -l, --list            List available box designs w/ samples\n");
2✔
105
    fprintf(st, "  -m, --mend            Mend (repair) box\n");
2✔
106
    fprintf(st, "  -n, --encoding <enc>  Character encoding of input and output [default: %s]\n", locale_charset());
2✔
107
    fprintf(st, "  -p, --padding <fmt>   Padding [default: none]\n");
2✔
108
    fprintf(st, "  -q, --tag-query <qry> Query the list of designs by tag\n");
2✔
109
    fprintf(st, "  -r, --remove          Remove box\n");
2✔
110
    fprintf(st, "  -s, --size <wxh>      Box size (width w and/or height h)\n");
2✔
111
    fprintf(st, "  -t, --tabs <str>      Tab stop distance and expansion [default: %de]\n", DEF_TABSTOP);
2✔
112
    fprintf(st, "  -v, --version         Print version information\n");
2✔
113
    /* fprintf(st, "  -x, --extra <arg>     If <arg> starts with "debug:", activate debug logging for specified log
114
                areas which follow in a comma-separated list [default area: MAIN]. If <arg> is "(undoc)", trigger
115
                undocumented behavior of design detail lister.");  // undocumented */
116

117
    bxs_free(config_file);
2✔
118
}
2✔
119

120

121

122
static opt_t *create_new_opt()
868✔
123
{
124
    opt_t *result = (opt_t *) calloc(1, sizeof(opt_t));
868✔
125
    if (result != NULL) {
868✔
126
        /* all values initialized with 0 or NULL */
127
        result->color = color_from_terminal;
868✔
128
        result->tabstop = DEF_TABSTOP;
868✔
129
        result->eol = "\n";      /* we must default to "\n" instead of EOL_DEFAULT as long as stdout is in text mode */
868✔
130
        result->tabexp = 'e';
868✔
131
        result->killblank = -1;
868✔
132
        result->debug = (int *) calloc(NUM_LOG_AREAS, sizeof(int));
868✔
133
        for (int i = 0; i < NUM_SIDES; ++i) {
4,340✔
134
            result->padding[i] = -1;
3,472✔
135
        }
1,736✔
136
    }
434✔
137
    else {
138
        perror(PROJECT);
×
139
    }
140
    return result;
868✔
141
}
142

143

144

145
/**
146
 * Alignment/positioning of text inside box.
147
 * @param result the options struct we are building
148
 * @param optarg the argument to `-a` on the command line
149
 * @returns 0 on success, anything else on error
150
 */
151
static int alignment(opt_t *result, char *optarg)
142✔
152
{
153
    int errfl = 1;
142✔
154
    char *p = optarg;
142✔
155

156
    while (*p) {
408✔
157
        errfl = 0;
276✔
158
        if (p[1] == '\0' && !strchr("lLcCrR", *p)) {
276✔
159
            errfl = 1;
2✔
160
            break;
2✔
161
        }
162

163
        switch (*p) {
274✔
164
            case 'h': case 'H':
41✔
165
                switch (p[1]) {
82✔
166
                    case 'c': case 'C': result->halign = 'c'; break;
26✔
167
                    case 'l': case 'L': result->halign = 'l'; break;
24✔
168
                    case 'r': case 'R': result->halign = 'r'; break;
30✔
169
                    default:            errfl = 1;            break;
2✔
170
                }
171
                ++p;
82✔
172
                break;
82✔
173

174
            case 'v': case 'V':
44✔
175
                switch (p[1]) {
88✔
176
                    case 'c': case 'C': result->valign = 'c'; break;
34✔
177
                    case 't': case 'T': result->valign = 't'; break;
24✔
178
                    case 'b': case 'B': result->valign = 'b'; break;
28✔
179
                    default:            errfl = 1;            break;
2✔
180
                }
181
                ++p;
88✔
182
                break;
88✔
183

184
            case 'j': case 'J':
30✔
185
                switch (p[1]) {
60✔
186
                    case 'l': case 'L': result->justify = 'l'; break;
18✔
187
                    case 'c': case 'C': result->justify = 'c'; break;
18✔
188
                    case 'r': case 'R': result->justify = 'r'; break;
22✔
189
                    default:            errfl = 1;             break;
2✔
190
                }
191
                ++p;
60✔
192
                break;
60✔
193

194
            case 'l': case 'L':
4✔
195
                result->justify = 'l';
8✔
196
                result->halign = 'l';
8✔
197
                result->valign = 'c';
8✔
198
                break;
8✔
199

200
            case 'r': case 'R':
6✔
201
                result->justify = 'r';
12✔
202
                result->halign = 'r';
12✔
203
                result->valign = 'c';
12✔
204
                break;
12✔
205

206
            case 'c': case 'C':
11✔
207
                result->justify = 'c';
22✔
208
                result->halign = 'c';
22✔
209
                result->valign = 'c';
22✔
210
                break;
22✔
211

212
            default:
1✔
213
                errfl = 1;
2✔
214
                break;
2✔
215
        }
216

217
        if (errfl) {
274✔
218
            break;
8✔
219
        } else {
220
            ++p;
266✔
221
        }
222
    }
223

224
    if (errfl) {
142✔
225
        bx_fprintf(stderr, "%s: Illegal text format -- %s\n", PROJECT, optarg);
12✔
226
        return 1;
12✔
227
    }
228
    return 0;
130✔
229
}
71✔
230

231

232

233
/**
234
 * Command line design definition.
235
 * @param result the options struct we are building
236
 * @param optarg the argument to `-c` on the command line
237
 * @returns 0 on success, anything else on error
238
 */
239
static int command_line_design(opt_t *result, char *optarg)
10✔
240
{
241
    if (strlen(optarg) == 0) {
10✔
242
        bx_fprintf(stderr, "%s: empty command line design definition\n", PROJECT);
2✔
243
        return 2;
2✔
244
    }
245
    result->cld = (char *) strdup(optarg);
8✔
246
    if (result->cld == NULL) {
8!
247
        perror(PROJECT);
×
248
        return 1;
×
249
    }
250
    result->design_choice_by_user = 1;
8✔
251
    return 0;
8✔
252
}
5✔
253

254

255

256
/**
257
 * Box design selection.
258
 * @param result the options struct we are building
259
 * @param optarg the argument to `-d` on the command line
260
 * @returns 0 on success, anything else on error
261
 */
262
static int design_choice(opt_t *result, char *optarg)
498✔
263
{
264
    BFREE (result->design);
498✔
265
    result->design = (design_t *) ((char *) strdup(optarg));
498✔
266
    if (result->design == NULL) {
498!
267
        perror(PROJECT);
×
268
        return 1;
×
269
    }
270
    result->design_choice_by_user = 1;
498✔
271
    return 0;
498✔
272
}
249✔
273

274

275

276
/**
277
 * EOL Override.
278
 * @param result the options struct we are building
279
 * @param optarg the argument to `-e` on the command line
280
 * @returns 0 on success, anything else on error
281
 */
282
static int eol_override(opt_t *result, char *optarg)
20✔
283
{
284
    result->eol_overridden = 1;
20✔
285
    if (strcasecmp(optarg, "CRLF") == 0) {
20✔
286
        result->eol = "\r\n";
8✔
287
    }
4✔
288
    else if (strcasecmp(optarg, "LF") == 0) {
12✔
289
        result->eol = "\n";
8✔
290
    }
4✔
291
    else if (strcasecmp(optarg, "CR") == 0) {
4✔
292
        result->eol = "\r";
2✔
293
    }
1✔
294
    else {
295
        bx_fprintf(stderr, "%s: invalid eol spec -- %s\n", PROJECT, optarg);
2✔
296
        return 1;
2✔
297
    }
298
    return 0;
18✔
299
}
10✔
300

301

302

303
/**
304
 * Indentation mode.
305
 * @param result the options struct we are building
306
 * @param optarg the argument to `-i` on the command line
307
 * @returns 0 on success, anything else on error
308
 */
309
static int indentation_mode(opt_t *result, char *optarg)
14✔
310
{
311
    size_t optlen = strlen(optarg);
14✔
312
    if (optlen <= 3 && !strncasecmp("box", optarg, optlen)) {
14✔
313
        result->indentmode = 'b';
4✔
314
    }
2✔
315
    else if (optlen <= 4 && !strncasecmp("text", optarg, optlen)) {
10✔
316
        result->indentmode = 't';
2✔
317
    }
1✔
318
    else if (optlen <= 4 && !strncasecmp("none", optarg, optlen)) {
8✔
319
        result->indentmode = 'n';
4✔
320
    }
2✔
321
    else {
322
        bx_fprintf(stderr, "%s: invalid indentation mode\n", PROJECT);
4✔
323
        return 1;
4✔
324
    }
325
    return 0;
10✔
326
}
7✔
327

328

329

330
/**
331
 * Kill blank lines or not [default: design-dependent].
332
 * @param result the options struct we are building
333
 * @param optarg the argument to `-k` on the command line
334
 * @returns 0 on success, anything else on error
335
 */
336
static int killblank(opt_t *result, char *optarg)
10✔
337
{
338
    if (result->killblank == -1) {
10✔
339
        if (strisyes(optarg)) {
8✔
340
            result->killblank = 1;
4✔
341
        }
2✔
342
        else if (strisno(optarg)) {
4✔
343
            result->killblank = 0;
2✔
344
        }
1✔
345
        else {
346
            bx_fprintf(stderr, "%s: -k: invalid parameter\n", PROJECT);
2✔
347
            return 1;
2✔
348
        }
349
    }
3✔
350
    return 0;
8✔
351
}
5✔
352

353

354

355
/**
356
 * Padding. Format is `([ahvtrbl]n)+`.
357
 * @param result the options struct we are building
358
 * @param optarg the argument to `-p` on the command line
359
 * @returns 0 on success, anything else on error
360
 */
361
static int padding(opt_t *result, char *optarg)
36✔
362
{
363
    int errfl = 1;
36✔
364
    char *p = optarg;
36✔
365

366
    while (*p) {
73✔
367
        errfl = 0;
45✔
368
        if (p[1] == '\0') {
45✔
369
            errfl = 1;
3✔
370
            break;
3✔
371
        }
372
        char c = *p;
42✔
373
        errno = 0;
42✔
374
        int size = (int) strtol(p + 1, &p, 10);
42✔
375
        if (errno || size < 0) {
42✔
376
            errfl = 1;
4✔
377
            break;
4✔
378
        }
379
        switch (c) {
38✔
380
            case 'a': case 'A':
2✔
381
                result->padding[BTOP] = size;
4✔
382
                result->padding[BBOT] = size;
4✔
383
                result->padding[BLEF] = size;
4✔
384
                result->padding[BRIG] = size;
4✔
385
                break;
4✔
386
            case 'h': case 'H':
4✔
387
                result->padding[BLEF] = size;
8✔
388
                result->padding[BRIG] = size;
8✔
389
                break;
8✔
390
            case 'v': case 'V':
1✔
391
                result->padding[BTOP] = size;
2✔
392
                result->padding[BBOT] = size;
2✔
393
                break;
2✔
394
            case 't': case 'T':
1✔
395
                result->padding[BTOP] = size;
2✔
396
                break;
2✔
397
            case 'l': case 'L':
7✔
398
                result->padding[BLEF] = size;
14✔
399
                break;
14✔
400
            case 'b': case 'B':
1✔
401
                result->padding[BBOT] = size;
2✔
402
                break;
2✔
403
            case 'r': case 'R':
3✔
404
                result->padding[BRIG] = size;
5✔
405
                break;
5✔
406
            default:
1✔
407
                errfl = 1;
1✔
408
                break;
1✔
409
        }
410
        if (errfl) {
38✔
411
            break;
1✔
412
        }
413
    }
414
    if (errfl) {
36✔
415
        bx_fprintf(stderr, "%s: invalid padding specification - %s\n", PROJECT, optarg);
10✔
416
        return 1;
10✔
417
    }
418
    return 0;
26✔
419
}
18✔
420

421

422

423
/**
424
 * Parse the tag query specified with `-q`.
425
 * @param result the options struct we are building
426
 * @param optarg the argument to `-q` on the command line
427
 * @returns 0 on success, anything else on error
428
 */
429
static int query(opt_t *result, char *optarg)
26✔
430
{
431
    char **query = parse_query(optarg);
26✔
432
    result->query = query;
26✔
433
    return query != NULL ? 0 : 1;
26✔
434
}
435

436

437

438
/**
439
 * Specify desired box target size.
440
 * @param result the options struct we are building
441
 * @param optarg the argument to `-s` on the command line
442
 * @returns 0 on success, anything else on error
443
 */
444
static int size_of_box(opt_t *result, char *optarg)
142✔
445
{
446
    char *p = strchr(optarg, 'x');
142✔
447
    if (!p) {
142✔
448
        p = strchr(optarg, 'X');
6✔
449
    }
3✔
450
    if (p) {
142✔
451
        *p = '\0';
136✔
452
    }
68✔
453
    errno = 0;
142✔
454
    if (optarg != p) {
142✔
455
        result->reqwidth = strtol(optarg, NULL, 10);
136✔
456
    }
68✔
457
    if (p) {
142✔
458
        result->reqheight = strtol(p + 1, NULL, 10);
136✔
459
        *p = 'x';
136✔
460
    }
68✔
461
    if (errno || (result->reqwidth == 0 && result->reqheight == 0) || result->reqwidth < 0 || result->reqheight < 0) {
142!
462
        bx_fprintf(stderr, "%s: invalid box size specification -- %s\n", PROJECT, optarg);
4✔
463
        return 1;
4✔
464
    }
465
    return 0;
138✔
466
}
71✔
467

468

469

470
static int debug_areas(opt_t *result, char *optarg)
×
471
{
472
    char *dup = NULL;
×
473
    if (optarg != NULL) {
×
474
        dup = strdup(optarg);   /* required because strtok() modifies its input */
×
475
    }
476
    else {
477
        dup = strdup(log_area_names[MAIN]);
×
478
    }
479

480
    for (char *a = strtok(dup, ","); a != NULL; a = strtok(NULL, ","))
×
481
    {
482
        char *trimmed = trimdup(a, a + strlen(a) - 1);
×
483
        if (strlen(trimmed) == 0) {
×
484
            BFREE(trimmed);
×
485
            continue;
×
486
        }
487
        int valid = 0;
×
488
        for (size_t i = ALL; i < NUM_LOG_AREAS + 2; i++) {
×
489
            if (strcasecmp(trimmed, log_area_names[i]) == 0) {
×
490
                valid = 1;
×
491
                if (i == ALL) {
×
492
                    for (size_t i = 0; i < NUM_LOG_AREAS; i++) {
×
493
                        result->debug[i] = 1;
×
494
                    }
495
                }
496
                else {
497
                    result->debug[i - 2] = 1;
×
498
                }
499
                break;
×
500
            }
501
        }
502
        if (!valid) {
×
503
            bx_fprintf(stderr, "%s: invalid debug area -- %s. Valid values: ", PROJECT, trimmed);
×
504
            for (size_t i = 1; i < NUM_LOG_AREAS + 2; i++) {
×
505
                bx_fprintf(stderr, "%s%s", log_area_names[i], i < (NUM_LOG_AREAS + 2 - 1) ? ", " : "\n");
×
506
            }
507
            BFREE(trimmed);
×
508
            return 1;
×
509
        }
510
        BFREE(trimmed);
×
511
    }
512
    BFREE(dup);
×
513
    return 0;
×
514
}
515

516

517

518
/**
519
 * Tab handling. Format is `n[eku]`.
520
 * @param result the options struct we are building
521
 * @param optarg the argument to `-t` on the command line
522
 * @returns 0 on success, anything else on error
523
 */
524
static int tab_handling(opt_t *result, char *optarg)
34✔
525
{
526
    char *p;
527
    int width = (int) strtol(optarg, &p, 10);
34✔
528
    if (width < 1 || width > MAX_TABSTOP) {
34✔
529
        bx_fprintf(stderr, "%s: invalid tab stop distance -- %d\n", PROJECT, width);
4✔
530
        return 1;
4✔
531
    }
532
    result->tabstop = width;
30✔
533

534
    int errfl = 0;
30✔
535
    if (*p != '\0') {
30✔
536
        if (p[1] != '\0') {
24✔
537
            errfl = 1;
2✔
538
        }
1✔
539
        else {
540
            switch (*p) {
22✔
541
                case 'e': case 'E':
1✔
542
                    result->tabexp = 'e';
2✔
543
                    break;
2✔
544
                case 'k': case 'K':
3✔
545
                    result->tabexp = 'k';
6✔
546
                    break;
6✔
547
                case 'u': case 'U':
6✔
548
                    result->tabexp = 'u';
12✔
549
                    break;
12✔
550
                default:
1✔
551
                    errfl = 1;
2✔
552
                    break;
2✔
553
            }
554
        }
555
    }
12✔
556
    if (errfl) {
30✔
557
        bx_fprintf(stderr, "%s: invalid tab handling specification - %s\n", PROJECT, optarg);
4✔
558
        return 1;
4✔
559
    }
560
    return 0;
26✔
561
}
17✔
562

563

564

565
/**
566
 * Handle undocumented options (-x).
567
 * @param result the options struct we are building
568
 * @param optarg the argument to `-x` on the command line
569
 * @returns 0 on success, anything else on error
570
 */
NEW
571
static int undocumented_options(opt_t *result, char *optarg)
×
572
{
NEW
573
    char *s = optarg;
×
574

NEW
575
    if (strncmp(s, EXTRA_UNDOC, strlen(EXTRA_UNDOC)) == 0) {
×
NEW
576
        result->qundoc = 1;
×
NEW
577
        s += strlen(EXTRA_UNDOC);
×
578
    }
579

NEW
580
    if (strncasecmp(s, EXTRA_DEBUG, strlen(EXTRA_DEBUG)) == 0) {
×
NEW
581
        s += strlen(EXTRA_DEBUG);
×
NEW
582
        if (*s == ':') {
×
NEW
583
            if (debug_areas(result, s + 1) != 0) {
×
NEW
584
                return 1;
×
585
            }
586
        }
NEW
587
        else if (*s == '\0') {
×
588
            /* default to MAIN */
NEW
589
            debug_areas(result, NULL);
×
590
        }
591
        else {
NEW
592
            bx_fprintf(stderr, "%s: invalid option -x %s\n", PROJECT, optarg);
×
NEW
593
            return 2;
×
594
        }
NEW
595
        activate_debug_logging(result->debug);
×
596
    }
597

NEW
598
    if (!result->qundoc && !is_debug_activated()) {
×
NEW
599
        bx_fprintf(stderr, "%s: invalid option -x %s\n", PROJECT, optarg);
×
NEW
600
        return 3;
×
601
    }
NEW
602
    return 0;
×
603
}
604

605

606

607
/**
608
 * Set *stdout* to binary mode, so that we can control the line terminator. This function only ever does anything on
609
 * Windows, because on Linux, we already do have control over line terminators.
610
 * @param result the options struct we are building
611
 * @return the *stdout* stream, reconfigured to binary if necessary
612
 */
613
static FILE *get_stdout_configured(opt_t *result)
800✔
614
{
615
    if (result->eol_overridden) {
800✔
616
        #ifdef __MINGW32__
617
            int rc = _setmode(fileno(stdout), _O_BINARY);
618
            if (rc == -1) {
619
                perror(PROJECT);
620
            }
621
        #endif
622
    }
9✔
623
    return stdout;
800✔
624
}
625

626

627

628
/**
629
 * Input and Output Files. After any command line options, an input file and an output file may be specified (in that
630
 * order). "-" may be substituted for standard input or output. A third file name would be invalid.
631
 * @param result the options struct we are building
632
 * @param argv the original command line options as specified
633
 * @param optind the index of the next element to be processed in `argv`
634
 * @returns 0 on success, anything else on error
635
 */
636
static int input_output_files(opt_t *result, char *argv[], int optind)
806✔
637
{
638
    if (argv[optind] == NULL) {                              /* neither infile nor outfile given */
806✔
639
        result->infile = stdin;
508✔
640
        result->outfile = get_stdout_configured(result);
508✔
641
    }
254✔
642

643
    else if (argv[optind + 1] && argv[optind + 2]) {         /* illegal third file */
298✔
644
        bx_fprintf(stderr, "%s: illegal parameter -- %s\n", PROJECT, argv[optind + 2]);
2✔
645
        usage_short(stderr);
2✔
646
        return 1;
2✔
647
    }
648

649
    else {
650
        if (strcmp(argv[optind], "-") == 0) {
296✔
651
            result->infile = stdin;                          /* use stdin for input */
4✔
652
        }
2✔
653
        else {
654
            result->infile = fopen(argv[optind], "r");
292✔
655
            if (result->infile == NULL) {
292✔
656
                bx_fprintf(stderr, "%s: Can\'t open input file -- %s\n", PROJECT, argv[optind]);
2✔
657
                return 9;                                    /* can't read infile */
2✔
658
            }
659
        }
660

661
        if (argv[optind + 1] == NULL) {
294✔
662
            result->outfile = get_stdout_configured(result); /* no outfile given */
290✔
663
        }
145✔
664
        else if (strcmp(argv[optind + 1], "-") == 0) {
4✔
665
            result->outfile = get_stdout_configured(result); /* use stdout for output */
2✔
666
        }
1✔
667
        else {
668
            result->outfile = fopen(argv[optind + 1], "wb");
2✔
669
            if (result->outfile == NULL) {
2✔
670
                perror(PROJECT);
×
671
                if (result->infile != stdin) {
×
672
                    fclose(result->infile);
×
673
                }
674
                return 10;
×
675
            }
676
        }
677
    }
678
    return 0;
802✔
679
}
403✔
680

681

682

683
static void print_debug_info(opt_t *result)
802✔
684
{
685
    if (result != NULL && is_debug_logging(MAIN)) {
802!
686
        log_debug(__FILE__, MAIN, "Command line option settings (excerpt):\n");
×
687
        log_debug(__FILE__, MAIN, "  - Alignment (-a): horiz %c, vert %c\n",
×
688
                result->halign ? result->halign : '?', result->valign ? result->valign : '?');
×
689
        log_debug(__FILE__, MAIN, "  - Line justification (-a): \'%c\'\n", result->justify ? result->justify : '?');
×
690
        log_debug(__FILE__, MAIN, "  - Design Definition W shape (-c): %s\n", result->cld ? result->cld : "n/a");
×
691
        log_debug(__FILE__, MAIN, "  - Color mode: %d\n", result->color);
×
692

693
        log_debug(__FILE__, MAIN, "  - Debug areas (-x debug:...): ");
×
694
        int dbgfirst = 1;
×
695
        for (size_t i = 0; i < NUM_LOG_AREAS; i++) {
×
696
            if (result->debug[i]) {
×
697
                log_debug_cont(MAIN, "%s%s", dbgfirst ? "" : ", ", log_area_names[i + 2]);
×
698
                dbgfirst = 0;
×
699
            }
700
        }
701
        log_debug_cont(MAIN, "%s\n", dbgfirst ? "(off)" : "");
×
702

703
        log_debug(__FILE__, MAIN, "  - Line terminator used (-e): %s\n",
×
704
            strcmp(result->eol, "\r\n") == 0 ? "CRLF" : (strcmp(result->eol, "\r") == 0 ? "CR" : "LF"));
×
705
        log_debug(__FILE__, MAIN, "  - Explicit config file (-f): %s\n", result->f ? result->f : "no");
×
706
        log_debug(__FILE__, MAIN, "  - Indentmode (-i): \'%c\'\n", result->indentmode ? result->indentmode : '?');
×
707
        log_debug(__FILE__, MAIN, "  - Kill blank lines (-k): %d\n", result->killblank);
×
708
        log_debug(__FILE__, MAIN, "  - Mend box (-m): %d\n", result->mend);
×
709
        log_debug(__FILE__, MAIN, "  - Padding (-p): l:%d t:%d r:%d b:%d\n",
×
710
                result->padding[BLEF], result->padding[BTOP], result->padding[BRIG], result->padding[BBOT]);
711

712
        log_debug(__FILE__, MAIN, "  - Tag Query (-q): ");
×
713
        if (result->query != NULL) {
×
714
            for (size_t qidx = 0; result->query[qidx] != NULL; ++qidx) {
×
715
                log_debug_cont(MAIN, "%s%s", qidx > 0 ? ", " : "", result->query[qidx]);
×
716
            }
717
        } else {
718
            log_debug_cont(MAIN, "(none)");
×
719
        }
720
        log_debug_cont(MAIN, "\n");
×
721

722
        log_debug(__FILE__, MAIN, "  - qundoc (-x): %d\n", result->qundoc);
×
723
        log_debug(__FILE__, MAIN, "  - Remove box (-r): %d\n", result->r);
×
724
        log_debug(__FILE__, MAIN, "  - Requested box size (-s): %ldx%ld\n", result->reqwidth, result->reqheight);
×
725
        log_debug(__FILE__, MAIN, "  - Tabstop distance (-t): %d\n", result->tabstop);
×
726
        log_debug(__FILE__, MAIN, "  - Tab handling (-t): \'%c\'\n", result->tabexp);
×
727
    }
728
}
802✔
729

730

731

732
opt_t *process_commandline(int argc, char *argv[])
868✔
733
{
734
    if (is_debug_logging(MAIN)) {
868✔
735
        log_debug(__FILE__, MAIN, "argc = %d\n", argc);
×
736
        log_debug(__FILE__, MAIN, "argv = [");
×
737
        for(int i=0; i<=argc; i++) {
×
738
            log_debug_cont(MAIN, "%s%s", argv[i], i < argc ? ", " : "]\n");
×
739
        }
740
    }
741

742
    opt_t *result = create_new_opt();
868✔
743

744
    /* Intercept '-?' case first, as it is not supported by getopt_long() */
745
    if (argc >= 2 && argv[1] != NULL && strcmp(argv[1], "-?") == 0) {
868!
746
        result->help = 1;
×
747
        return result;
×
748
    }
749

750
    optind = 1;   /* ensure that getopt() will process all arguments, even in unit test situations */
868✔
751
    int option_index = 0;
868✔
752
    const struct option long_options[] = {
868✔
753
        { "align",         required_argument, NULL, 'a' },
754
        { "create",        required_argument, NULL, 'c' },
755
        { "color",         no_argument,       NULL, OPT_COLOR },
756
        { "no-color",      no_argument,       NULL, OPT_NO_COLOR },
757
        { "design",        required_argument, NULL, 'd' },
758
        { "eol",           required_argument, NULL, 'e' },
759
        { "config",        required_argument, NULL, 'f' },
760
        { "help",          no_argument,       NULL, 'h' },
761
        { "indent",        required_argument, NULL, 'i' },
762
        { "kill-blank",    no_argument,       NULL, OPT_KILLBLANK },
763
        { "no-kill-blank", no_argument,       NULL, OPT_NO_KILLBLANK },
764
        { "list",          no_argument,       NULL, 'l' },
765
        { "mend",          no_argument,       NULL, 'm' },
766
        { "encoding",      required_argument, NULL, 'n' },
767
        { "padding",       required_argument, NULL, 'p' },
768
        { "tag-query",     required_argument, NULL, 'q' },
769
        { "remove",        no_argument,       NULL, 'r' },
770
        { "size",          required_argument, NULL, 's' },
771
        { "tabs",          required_argument, NULL, 't' },
772
        { "version",       no_argument,       NULL, 'v' },
773
        { "extra",         required_argument, NULL, 'x' },
774
        { NULL,            0,                 NULL,  0  }
775
    };
776
    const char *short_options = "a:c:d:e:f:hi:k:lmn:p:q:rs:t:vx:";
868✔
777

778
    int oc;   /* option character */
779
    do {
434✔
780
        oc = getopt_long(argc, argv, short_options, long_options, &option_index);
2,696✔
781

782
        switch (oc) {
2,696✔
783

784
            case 'a':
71✔
785
                if (alignment(result, optarg) != 0) {
142✔
786
                    BFREE(result);
12!
787
                    return NULL;
12✔
788
                }
789
                break;
130✔
790

791
            case 'c':
5✔
792
                if (command_line_design(result, optarg) != 0) {
10✔
793
                    BFREE(result);
2!
794
                    return NULL;
2✔
795
                }
796
                break;
8✔
797
            
798
            case OPT_COLOR:
1✔
799
                result->color = force_ansi_color;
2✔
800
                break;
2✔
801

802
            case OPT_NO_COLOR:
803
                result->color = force_monochrome;
×
804
                break;
×
805

806
            case 'd':
249✔
807
                if (design_choice(result, optarg) != 0) {
498✔
808
                    BFREE(result);
×
809
                    return NULL;
×
810
                }
811
                break;
498✔
812

813
            case 'e':
10✔
814
                if (eol_override(result, optarg) != 0) {
20✔
815
                    BFREE(result);
2!
816
                    return NULL;
2✔
817
                }
818
                break;
18✔
819

820
            case 'f':
270✔
821
                result->f = strdup(optarg);   /* input file */
540✔
822
                break;
540✔
823
            
824
            case 'h':
2✔
825
                result->help = 1;
4✔
826
                return result;
4✔
827

828
            case 'i':
7✔
829
                if (indentation_mode(result, optarg) != 0) {
14✔
830
                    BFREE(result);
4!
831
                    return NULL;
4✔
832
                }
833
                break;
10✔
834

835
            case 'k':
5✔
836
                if (killblank(result, optarg) != 0) {
10✔
837
                    BFREE(result);
2!
838
                    return NULL;
2✔
839
                }
840
                break;
8✔
841
            
842
            case OPT_KILLBLANK:
2✔
843
                if (result->killblank == -1) {
4✔
844
                    result->killblank = 1;
4✔
845
                }
2✔
846
                break;
4✔
847

848
            case OPT_NO_KILLBLANK:
1✔
849
                if (result->killblank == -1) {
2✔
850
                    result->killblank = 0;
2✔
851
                }
1✔
852
                break;
2✔
853

854
            case 'l':
25✔
855
                result->l = 1;   /* list available box styles */
50✔
856
                break;
50✔
857

858
            case 'm':
73✔
859
                result->mend = 2;   /*  Mend box: remove, then redraw */
146✔
860
                result->r = 1;
146✔
861
                result->killblank = 0;
146✔
862
                break;
146✔
863

864
            case 'n':
12✔
865
                result->encoding = strdup(optarg);   /* character encoding */
24✔
866
                if (result->encoding == NULL) {
24✔
867
                    perror(PROJECT);
×
868
                    BFREE(result);
×
869
                    return NULL;
×
870
                }
871
                break;
24✔
872

873
            case 'p':
18✔
874
                if (padding(result, optarg) != 0) {
36✔
875
                    BFREE(result);
10!
876
                    return NULL;
10✔
877
                }
878
                break;
26✔
879

880
            case 'q':
13✔
881
                if (query(result, optarg) != 0) {
26✔
882
                    BFREE(result);
8!
883
                    return NULL;
8✔
884
                }
885
                break;
18✔
886

887
            case 'r':
90✔
888
                result->r = 1;   /* remove box */
180✔
889
                break;
180✔
890

891
            case 's':
71✔
892
                if (size_of_box(result, optarg) != 0) {
142✔
893
                    BFREE(result);
4!
894
                    return NULL;
4✔
895
                }
896
                break;
138✔
897

898
            case 't':
17✔
899
                if (tab_handling(result, optarg) != 0) {
34✔
900
                    BFREE(result);
8!
901
                    return NULL;
8✔
902
                }
903
                break;
26✔
904

905
            case 'v':
1✔
906
                result->version_requested = 1;   /* print version number */
2✔
907
                return result;
2✔
908

909
            case 'x':
NEW
910
                if (undocumented_options(result, optarg) != 0) {
×
NEW
911
                    BFREE(result);
×
NEW
912
                    return NULL;
×
913
                }
914
                break;
×
915

916
            case ':':
2✔
917
            case '?':
918
                /* Missing argument or illegal option - do nothing else */
919
                usage_short(stderr);
4✔
920
                BFREE(result);
4✔
921
                return NULL;
4✔
922

923
            case EOF:
403✔
924
                /* End of list, do nothing more */
925
                break;
806✔
926

927
            default:
928
                bx_fprintf(stderr, "%s: internal error\n", PROJECT);
×
929
                BFREE(result);
×
930
                return NULL;
×
931
        }
932
    } while (oc != EOF);
2,634✔
933

934
    if (input_output_files(result, argv, optind) != 0) {
806✔
935
        BFREE(result);
4✔
936
        return NULL;
4✔
937
    }
938

939
    print_debug_info(result);
802✔
940
    return result;
802✔
941
}
434✔
942

943

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