Bug Summary

File:_build/../src/parser-cat.cc
Warning:line 807, column 39
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name parser-cat.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/_build -fcoverage-compilation-dir=/rootdir/_build -resource-dir /usr/lib/llvm-21/lib/clang/21 -I src/parser-cat.p -I src -I ../src -I . -I .. -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D _GLIBCXX_ASSERTIONS=1 -D _FILE_OFFSET_BITS=64 -D PARSER_INCLUDE_NOP -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/x86_64-linux-gnu/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-address-of-packed-member -Wno-missing-field-initializers -Wno-packed -Wno-switch-enum -Wno-unused-parameter -Wwrite-strings -std=gnu++17 -fdeprecated-macro -ferror-limit 19 -fvisibility=hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-checker deadcode.DeadStores -analyzer-checker security.ArrayBound -analyzer-checker unix.cstring.NotNullTerminated -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2026-01-19-193148-10936-1 -x c++ ../src/parser-cat.cc
1/*
2 * Copyright © 2017, 2018 Christian Persch
3 *
4 * This programme is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This programme is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include <glib.h>
21#include <locale.h>
22#include <unistd.h>
23#include <fcntl.h>
24
25#include <cassert>
26#include <cstring>
27#include <cerrno>
28#include <cstdio>
29#include <cstdlib>
30
31#include <string>
32
33#include "debug.h"
34#include "glib-glue.hh"
35#include "libc-glue.hh"
36#include "parser.hh"
37#include "parser-glue.hh"
38#include "utf8.hh"
39
40enum {
41#define _BTE_SGR(...)
42#define _BTE_NGR(name, value) BTE_SGR_##name = value,
43#include "parser-sgr.hh"
44#undef _BTE_SGR
45#undef _BTE_NGR
46};
47
48using namespace std::literals;
49
50char*
51bte::parser::Sequence::ucs4_to_utf8(gunichar const* str,
52 ssize_t len) const noexcept
53{
54 return g_ucs4_to_utf8(str, len, nullptr, nullptr, nullptr);
55}
56
57static constexpr char const*
58seq_to_str(unsigned int type) noexcept
59{
60 switch (type) {
61 case BTE_SEQ_NONE: return "NONE";
62 case BTE_SEQ_IGNORE: return "IGNORE";
63 case BTE_SEQ_GRAPHIC: return "GRAPHIC";
64 case BTE_SEQ_CONTROL: return "CONTROL";
65 case BTE_SEQ_ESCAPE: return "ESCAPE";
66 case BTE_SEQ_CSI: return "CSI";
67 case BTE_SEQ_DCS: return "DCS";
68 case BTE_SEQ_OSC: return "OSC";
69 case BTE_SEQ_SCI: return "SCI";
70 case BTE_SEQ_APC: return "APC";
71 case BTE_SEQ_PM: return "PM";
72 case BTE_SEQ_SOS: return "SOS";
73 default:
74 assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail (
"false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
75 }
76}
77
78static constexpr char const*
79cmd_to_str(unsigned int command) noexcept
80{
81 switch (command) {
82#define _BTE_CMD(cmd) case BTE_CMD_##cmd: return #cmd;
83#define _BTE_NOP(cmd) _BTE_CMD(cmd)
84#include "parser-cmd.hh"
85#undef _BTE_CMD
86#undef _BTE_NOP
87 default:
88 return nullptr;
89 }
90}
91
92#if 0
93static constexepr char const*
94charset_alias_to_str(unsigned int cs) noexcept
95{
96 switch (cs) {
97#define _BTE_CHARSET_PASTE(name)
98#define _BTE_CHARSET(name) _BTE_CHARSET_PASTE(name)
99#define _BTE_CHARSET_ALIAS_PASTE(name1,name2) case BTE_CHARSET_##name1: return #name1 "(" ## #name2 ## ")";
100#define _BTE_CHARSET_ALIAS(name1,name2)
101#include "parser-charset.hh"
102#undef _BTE_CHARSET_PASTE
103#undef _BTE_CHARSET
104#undef _BTE_CHARSET_ALIAS_PASTE
105#undef _BTE_CHARSET_ALIAS
106 default:
107 return nullptr; /* not an alias */
108 }
109}
110
111static constexpr char const*
112charset_to_str(unsigned int cs) noexcept
113{
114 auto alias = charset_alias_to_str(cs);
115 if (alias)
116 return alias;
117
118 switch (cs) {
119#define _BTE_CHARSET_PASTE(name) case BTE_CHARSET_##name: return #name;
120#define _BTE_CHARSET(name) _BTE_CHARSET_PASTE(name)
121#define _BTE_CHARSET_ALIAS_PASTE(name1,name2)
122#define _BTE_CHARSET_ALIAS(name1,name2)
123#include "parser-charset.hh"
124#undef _BTE_CHARSET_PASTE
125#undef _BTE_CHARSET
126#undef _BTE_CHARSET_ALIAS_PASTE
127#undef _BTE_CHARSET_ALIAS
128 default:
129 static char buf[32];
130 snprintf(buf, sizeof(buf), "UNKOWN(%u)", cs);
131 return buf;
132 }
133}
134#endif
135
136class PrettyPrinter {
137private:
138 std::string m_str;
139 bool m_plain;
140 bool m_codepoints;
141
142 inline constexpr bool plain() const noexcept { return m_plain; }
143
144 class Attribute {
145 public:
146 Attribute(PrettyPrinter* printer,
147 std::string const& intro,
148 std::string const& outro) noexcept
149 : m_printer{printer}
150 , m_outro{outro} {
151 if (!m_printer->plain())
152 m_printer->m_str.append(intro);
153 }
154
155 ~Attribute() noexcept
156 {
157 if (!m_printer->plain())
158 m_printer->m_str.append(m_outro);
159 }
160
161 private:
162 PrettyPrinter* m_printer;
163 std::string m_outro;
164 }; // class Attribute
165
166 class ReverseAttr : private Attribute {
167 public:
168 ReverseAttr(PrettyPrinter* printer)
169 : Attribute(printer, "\e[7m"s, "\e[27m"s)
170 { }
171 };
172
173 class RedAttr : private Attribute {
174 public:
175 RedAttr(PrettyPrinter* printer)
176 : Attribute(printer, "\e[7;31m"s, "\e[27;39m"s)
177 { }
178 };
179
180 void
181 print_params(bte::parser::Sequence const& seq) noexcept
182 {
183 auto const size = seq.size();
184 if (size > 0)
185 m_str.push_back(' ');
186
187 for (unsigned int i = 0; i < size; i++) {
188 if (!seq.param_default(i))
189 print_format("%d", seq.param(i));
190 if (i + 1 < size)
191 m_str.push_back(seq.param_nonfinal(i) ? ':' : ';');
192 }
193 }
194
195 void
196 print_pintro(bte::parser::Sequence const& seq) noexcept
197 {
198 auto const type = seq.type();
199 if (type != BTE_SEQ_CSI &&
200 type != BTE_SEQ_DCS)
201 return;
202
203 auto const p = seq.intermediates() & 0x7;
204 if (p == 0)
205 return;
206
207 m_str.push_back(' ');
208 m_str.push_back(char(0x40 - p));
209 }
210
211 void
212 print_intermediates(bte::parser::Sequence const& seq) noexcept
213 {
214 auto const type = seq.type();
215 auto intermediates = seq.intermediates();
216 if (type == BTE_SEQ_CSI ||
217 type == BTE_SEQ_DCS)
218 intermediates = intermediates >> 3; /* remove pintro */
219
220 while (intermediates != 0) {
221 unsigned int i = intermediates & 0x1f;
222 char c = 0x20 + i - 1;
223
224 m_str.push_back(' ');
225 if (c == 0x20)
226 m_str.append("SP"s);
227 else
228 m_str.push_back(c);
229
230 intermediates = intermediates >> 5;
231 }
232 }
233
234 void
235 print_unichar(uint32_t c) noexcept
236 {
237 char buf[7];
238 auto len = g_unichar_to_utf8(c, buf);
239 m_str.append(buf, len);
240 }
241
242 G_GNUC_PRINTF(2, 3)__attribute__((__format__ (__printf__, 2, 3)))
243 void
244 print_format(char const* format,
245 ...)
246 {
247 char buf[256];
248 va_list args;
249 va_start(args, format)__builtin_va_start(args, format);
250 auto len = g_vsnprintf(buf, sizeof(buf), format, args);
251 va_end(args)__builtin_va_end(args);
252
253 m_str.append(buf, len);
254 }
255
256 void
257 print_string(bte::parser::Sequence const& seq) noexcept
258 {
259 auto u8str = seq.string_param();
260
261 m_str.push_back('\"');
262 m_str.append(u8str);
263 m_str.push_back('\"');
264
265 g_free(u8str);
266 }
267
268 void
269 print_seq_and_params(bte::parser::Sequence const& seq) noexcept
270 {
271 ReverseAttr attr(this);
272
273 if (seq.command() != BTE_CMD_NONE) {
274 m_str.push_back('{');
275 m_str.append(cmd_to_str(seq.command()));
276 print_params(seq);
277 m_str.push_back('}');
278 } else {
279 m_str.push_back('{');
280 m_str.append(seq_to_str(seq.type()));
281 print_pintro(seq);
282 print_params(seq);
283 print_intermediates(seq);
284 m_str.push_back(' ');
285 m_str.push_back(seq.terminator());
286 m_str.push_back('}');
287 }
288 }
289
290 void
291 print_seq(bte::parser::Sequence const& seq) noexcept
292 {
293 switch (seq.type()) {
294 case BTE_SEQ_NONE: {
295 RedAttr attr(this);
296 m_str.append("{NONE}"s);
297 break;
298 }
299
300 case BTE_SEQ_IGNORE: {
301 RedAttr attr(this);
302 m_str.append("{IGNORE}"s);
303 break;
304 }
305
306 case BTE_SEQ_GRAPHIC: {
307 auto const terminator = seq.terminator();
308 bool const printable = g_unichar_isprint(terminator);
309 if (m_codepoints || !printable) {
310 if (printable) {
311 char ubuf[7];
312 ubuf[g_unichar_to_utf8(terminator, ubuf)] = 0;
313 print_format("[%04X %s]", terminator, ubuf);
314 } else {
315 print_format("[%04X]", terminator);
316 }
317 } else {
318 print_unichar(terminator);
319 }
320 break;
321 }
322
323 case BTE_SEQ_CONTROL:
324 case BTE_SEQ_ESCAPE: {
325 ReverseAttr attr(this);
326 print_format("{%s}", cmd_to_str(seq.command()));
327 break;
328 }
329
330 case BTE_SEQ_CSI:
331 case BTE_SEQ_DCS: {
332 print_seq_and_params(seq);
333 break;
334 }
335
336 case BTE_SEQ_OSC: {
337 ReverseAttr attr(this);
338 m_str.append("{OSC "s);
339 print_string(seq);
340 m_str.push_back('}');
341 break;
342 }
343
344 case BTE_SEQ_SCI: {
345 auto const terminator = seq.terminator();
346 if (terminator <= 0x20)
347 print_format("{SCI %d/%d}",
348 terminator / 16,
349 terminator % 16);
350 else
351 print_format("{SCI %c}", terminator);
352 break;
353 }
354
355 default:
356 assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail (
"false", __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
357 }
358 }
359
360 void
361 printout() noexcept
362 {
363 m_str.push_back('\n');
364#pragma GCC diagnostic push
365#pragma GCC diagnostic ignored "-Wunused-result"
366 write(STDOUT_FILENO1, m_str.data(), m_str.size());
367#pragma GCC diagnostic pop
368 m_str.clear();
369 }
370
371public:
372
373 PrettyPrinter(bool plain,
374 bool codepoints) noexcept
375 : m_plain{plain}
376 , m_codepoints{codepoints}
377 {
378 }
379
380 ~PrettyPrinter() noexcept
381 {
382 printout();
383 }
384
385 void operator()(bte::parser::Sequence const& seq) noexcept
386 {
387 print_seq(seq);
388 if (seq.command() == BTE_CMD_LF)
389 printout();
390 }
391
392}; // class PrettyPrinter
393
394class Linter {
395private:
396 G_GNUC_PRINTF(2, 3)__attribute__((__format__ (__printf__, 2, 3)))
397 void
398 warn(char const* format,
399 ...) const noexcept
400 {
401 va_list args;
402 va_start(args, format)__builtin_va_start(args, format);
403 char* str = g_strdup_vprintf(format, args);
404 va_end(args)__builtin_va_end(args);
405 g_printerr("WARNING: %s\n", str);
406 g_free(str);
407 }
408
409 void
410 warn_deprecated(int cmd,
411 int replacement_cmd) const noexcept
412 {
413 warn("%s is deprecated; use %s instead",
414 cmd_to_str(cmd),
415 cmd_to_str(replacement_cmd));
416 }
417
418 void
419 check_sgr_number(int sgr) noexcept
420 {
421 switch (sgr) {
422 case -1:
423#define _BTE_SGR(name, value) case value:
424#define _BTE_NGR(...)
425#include "parser-sgr.hh"
426#undef _BTE_SGR
427#undef _BTE_NGR
428 case BTE_SGR_SET_FORE_LEGACY_START+1 ... BTE_SGR_SET_FORE_LEGACY_END-1:
429 case BTE_SGR_SET_FORE_LEGACY_BRIGHT_START+1 ... BTE_SGR_SET_FORE_LEGACY_BRIGHT_END-1:
430 case BTE_SGR_SET_BACK_LEGACY_START+1 ... BTE_SGR_SET_BACK_LEGACY_END-1:
431 case BTE_SGR_SET_BACK_LEGACY_BRIGHT_START+1 ... BTE_SGR_SET_BACK_LEGACY_BRIGHT_END-1:
432 break;
433
434#define _BTE_SGR(...)
435#define _BTE_NGR(name, value) case value:
436#include "parser-sgr.hh"
437#undef _BTE_SGR
438#undef _BTE_NGR
439 case BTE_SGR_SET_FONT_FIRST+1 ... BTE_SGR_SET_FONT_LAST-1:
440 warn("SGR %d is unsupported", sgr);
441 break;
442
443 default:
444 warn("SGR %d is unknown", sgr);
445 break;
446
447 }
448 }
449
450 void
451 check_sgr_color(bte::parser::Sequence const& seq,
452 unsigned int& idx) noexcept
453 {
454 auto const sgr = seq.param(idx);
455
456 /* Simplified and adapted from Terminal::seq_parse_sgr_color() */
457 if (seq.param_nonfinal(idx)) {
458 /* Colon version */
459 auto const param = seq.param(++idx);
460 switch (param) {
461 case 2: {
462 auto const n = seq.next(idx) - idx;
463 if (n < 4)
464 warn("SGR %d:2 not enough parameters", sgr);
465 else if (n == 4)
466 warn("SGR %d:2:r:g:b is deprecated; use SGR %d:2::r:g:b instead",
467 sgr, sgr);
468 break;
469 }
470 case 5: {
471 auto const n = seq.next(idx) - idx;
472 if (n < 2)
473 warn("SGR %d:5 not enough parameters", sgr);
474 break;
475 }
476 case -1:
477 warn("SGR %d does not admit default parameters", sgr);
478 break;
479 case 0:
480 case 1:
481 case 3:
482 case 4:
483 warn("SGR %d:%d is unsupported", sgr, param);
484 break;
485 default:
486 warn("SGR %d:%d is unknown", sgr, param);
487 }
488 } else {
489 /* Semicolon version */
490 idx = seq.next(idx);
491 auto const param = seq.param(idx);
492 switch (param) {
493 case 2:
494 /* Consume 3 more parameters */
495 idx = seq.next(idx);
496 idx = seq.next(idx);
497 idx = seq.next(idx);
498 warn("SGR %d;%d;r;g;b is deprecated; use SGR %d:%d::r:g:b instead",
499 sgr, param, sgr, param);
500 break;
501 case 5:
502 /* Consume 1 more parameter */
503 idx = seq.next(idx);
504 warn("SGR %d;%d;index is deprecated; use SGR %d:%d:index instead",
505 sgr, param, sgr, param);
506 break;
507 case -1:
508 warn("SGR %d does not admit default parameters", sgr);
509 break;
510 case 0:
511 case 1:
512 case 3:
513 case 4:
514 warn("SGR %d;%d;... is unsupported; use SGR %d:%d:... instead",
515 sgr, param, sgr, param);
516 break;
517 default:
518 warn("SGR %d;%d is unknown", sgr, param);
519 break;
520 }
521 }
522 }
523
524 void
525 check_sgr_underline(bte::parser::Sequence const& seq,
526 unsigned int idx) noexcept
527 {
528 auto const sgr = seq.param(idx);
529
530 int param = 1;
531 /* If we have a subparameter, get it */
532 if (seq.param_nonfinal(idx))
533 param = seq.param(idx + 1);
534
535 switch (param) {
536 case -1:
537 case 0:
538 case 1:
539 case 2:
540 case 3:
541 break;
542 case 4:
543 case 5:
544 warn("SGR %d:%d is unsupported", sgr, param);
545 break;
546 default:
547 warn("SGR %d:%d is unknown", sgr, param);
548 break;
549 }
550 }
551
552 void
553 check_sgr(bte::parser::Sequence const& seq) noexcept
554 {
555 for (unsigned int i = 0; i < seq.size(); i = seq.next(i)) {
556 auto const param = seq.param(i, 0);
557
558 check_sgr_number(param);
559
560 switch (param) {
561 case BTE_SGR_SET_UNDERLINE:
562 check_sgr_underline(seq, i);
563 break;
564
565 case BTE_SGR_SET_FORE_SPEC:
566 case BTE_SGR_SET_BACK_SPEC:
567 case BTE_SGR_SET_DECO_SPEC:
568 check_sgr_color(seq, i);
569 break;
570
571 default:
572 if (seq.param_nonfinal(i))
573 warn("SGR %d does not admit subparameters", param);
574 break;
575 }
576 }
577 }
578
579public:
580 constexpr Linter() noexcept = default;
581 ~Linter() noexcept = default;
582
583 void operator()(bte::parser::Sequence const& seq) noexcept
584 {
585 auto cmd = seq.command();
586 switch (cmd) {
587 case BTE_CMD_OSC:
588 if (seq.terminator() == 7 /* BEL */)
589 warn("OSC terminated by BEL may be ignored; use ST (ESC \\) instead.");
590 break;
591
592 case BTE_CMD_DECSLRM_OR_SCOSC:
593 cmd = BTE_CMD_SCOSC;
594 [[fallthrough]];
595 case BTE_CMD_SCOSC:
596 warn_deprecated(cmd, BTE_CMD_DECSC);
597 break;
598
599 case BTE_CMD_SCORC:
600 warn_deprecated(cmd, BTE_CMD_DECRC);
601 break;
602
603 case BTE_CMD_SGR:
604 check_sgr(seq);
605 break;
606
607 default:
608 if (cmd >= BTE_CMD_NOP_FIRST)
609 warn("%s is unimplemented", cmd_to_str(cmd));
610 break;
611 }
612 }
613
614}; // class Linter
615
616class Sink {
617public:
618 void operator()(bte::parser::Sequence const& seq) noexcept { }
619
620}; // class Sink
621
622class Processor {
623private:
624 gsize m_seq_stats[BTE_SEQ_N];
625 gsize m_cmd_stats[BTE_CMD_N];
626 GArray* m_bench_times;
627
628 template<class Functor>
629 void
630 process_file_utf8(int fd,
631 Functor& func)
632 {
633 bte::parser::Parser parser{};
634 bte::parser::Sequence seq{parser};
635
636 gsize const buf_size = 16384;
637 guchar* buf = g_new0(guchar, buf_size)(guchar *) (__extension__ ({ gsize __n = (gsize) (buf_size); gsize
__s = sizeof (guchar); gpointer __p; if (__s == 1) __p = g_malloc0
(__n); else if (__builtin_constant_p (__n) && (__s ==
0 || __n <= (9223372036854775807L *2UL+1UL) / __s)) __p =
g_malloc0 (__n * __s); else __p = g_malloc0_n (__n, __s); __p
; }))
;
638
639 auto start_time = g_get_monotonic_time();
640
641 bte::base::UTF8Decoder decoder;
642
643 gsize buf_start = 0;
644 for (;;) {
645 auto len = read(fd, buf + buf_start, buf_size - buf_start);
646 if (!len)
647 break;
648 if (len == -1) {
649 if (errno(*__errno_location ()) == EAGAIN11)
650 continue;
651 break;
652 }
653
654 auto const bufend = buf + len;
655 for (auto sptr = buf; sptr < bufend; ++sptr) {
656 switch (decoder.decode(*sptr)) {
657 case bte::base::UTF8Decoder::REJECT_REWIND:
658 /* Rewind the stream.
659 * Note that this will never lead to a loop, since in the
660 * next round this byte *will* be consumed.
661 */
662 --sptr;
663 [[fallthrough]];
664 case bte::base::UTF8Decoder::REJECT:
665 decoder.reset();
666 /* Fall through to insert the U+FFFD replacement character. */
667 [[fallthrough]];
668 case bte::base::UTF8Decoder::ACCEPT: {
669 auto ret = parser.feed(decoder.codepoint());
670 if (G_UNLIKELY(ret < 0)(__builtin_expect (__extension__ ({ int _g_boolean_var_10 = 0
; if (ret < 0) _g_boolean_var_10 = 1; _g_boolean_var_10; }
), 0))
) {
671 g_printerr("Parser error!\n");
672 goto out;
673 }
674
675 m_seq_stats[ret]++;
676 if (ret != BTE_SEQ_NONE) {
677 m_cmd_stats[seq.command()]++;
678 func(seq);
679 }
680 break;
681 }
682
683 default:
684 break;
685 }
686 }
687 }
688
689 out:
690
691 int64_t time_spent = g_get_monotonic_time() - start_time;
692 g_array_append_val(m_bench_times, time_spent)g_array_append_vals (m_bench_times, &(time_spent), 1);
693
694 g_free(buf);
695 }
696
697 template<class Functor>
698 bool
699 process_file(int fd,
700 int repeat,
701 Functor& func)
702 {
703 if (fd == STDIN_FILENO0 && repeat != 1) {
704 g_printerr("Cannot consume STDIN more than once\n");
705 return false;
706 }
707
708 for (auto i = 0; i < repeat; ++i) {
709 if (i > 0 && lseek(fd, 0, SEEK_SET0) != 0) {
710 auto errsv = bte::libc::ErrnoSaver{};
711 g_printerr("Failed to seek: %s\n", g_strerror(errsv));
712 return false;
713 }
714
715 process_file_utf8(fd, func);
716 }
717
718 return true;
719 }
720
721public:
722
723 Processor() noexcept
724 {
725 memset(&m_seq_stats, 0, sizeof(m_seq_stats));
726 memset(&m_cmd_stats, 0, sizeof(m_cmd_stats));
727 m_bench_times = g_array_new(false, true, sizeof(int64_t));
728 }
729
730 ~Processor() noexcept
731 {
732 g_array_free(m_bench_times, true);
733 }
734
735 template<class Functor>
736 bool
737 process_files(char const* const* filenames,
738 int repeat,
739 Functor& func)
740 {
741 bool r = true;
742 if (filenames != nullptr) {
743 for (auto i = 0; filenames[i] != nullptr; i++) {
744 char const* filename = filenames[i];
745
746 int fd = -1;
747 if (g_str_equal(filename, "-")(strcmp ((const char *) (filename), (const char *) ("-")) == 0
)
) {
748 fd = STDIN_FILENO0;
749 } else {
750 fd = open(filename, O_RDONLY00);
751 if (fd == -1) {
752 auto errsv = bte::libc::ErrnoSaver{};
753 g_printerr("Error opening file %s: %s\n",
754 filename, g_strerror(errsv));
755 }
756 }
757 if (fd != -1) {
758 r = process_file(fd, repeat, func);
759 if (fd != STDIN_FILENO0)
760 close(fd);
761 if (!r)
762 break;
763 }
764 }
765 } else {
766 r = process_file(STDIN_FILENO0, repeat, func);
767 }
768
769 return r;
770 }
771
772 void print_statistics() const noexcept
773 {
774 for (unsigned int s = BTE_SEQ_NONE + 1; s < BTE_SEQ_N; s++) {
775 g_printerr("%\'16" G_GSIZE_FORMAT"lu" " %s\n", m_seq_stats[s], seq_to_str(s));
776 }
777
778 g_printerr("\n");
779 for (unsigned int s = 0; s < BTE_CMD_N; s++) {
780 if (m_cmd_stats[s] > 0) {
781 g_printerr("%\'16" G_GSIZE_FORMAT"lu" " %s%s\n",
782 m_cmd_stats[s],
783 cmd_to_str(s),
784 s >= BTE_CMD_NOP_FIRST ? " [NOP]" : "");
785 }
786 }
787 }
788
789 void print_benchmark() const noexcept
790 {
791 g_array_sort(m_bench_times,
10
Value assigned to field 'len'
792 [](void const* p1, void const* p2) -> int {
793 int64_t const t1 = *(int64_t const*)p1;
794 int64_t const t2 = *(int64_t const*)p2;
795 return t1 == t2 ? 0 : (t1 < t2 ? -1 : 1);
796 });
797
798 int64_t total_time = 0;
799 for (unsigned int i = 0; i < m_bench_times->len; ++i)
11
Assuming 'i' is >= field 'len'
12
Loop condition is false. Execution continues on line 802
800 total_time += g_array_index(m_bench_times, int64_t, i)(((int64_t*) (void *) (m_bench_times)->data) [(i)]);
801
802 g_printerr("\nTimes: best %\'" G_GINT64_FORMAT"li" "µs "
803 "worst %\'" G_GINT64_FORMAT"li" "µs "
804 "average %\'" G_GINT64_FORMAT"li" "µs\n",
805 g_array_index(m_bench_times, int64_t, 0)(((int64_t*) (void *) (m_bench_times)->data) [(0)]),
806 g_array_index(m_bench_times, int64_t, m_bench_times->len - 1)(((int64_t*) (void *) (m_bench_times)->data) [(m_bench_times
->len - 1)])
,
807 total_time / (int64_t)m_bench_times->len);
13
Division by zero
808 for (unsigned int i = 0; i < m_bench_times->len; ++i)
809 g_printerr(" %\'" G_GINT64_FORMAT"li" "µs\n",
810 g_array_index(m_bench_times, int64_t, i)(((int64_t*) (void *) (m_bench_times)->data) [(i)]));
811 }
812
813}; // class Processor
814
815class Options {
816private:
817 bool m_benchmark{false};
818 bool m_codepoints{false};
819 bool m_lint{false};
820 bool m_plain{false};
821 bool m_quiet{false};
822 bool m_statistics{false};
823 int m_repeat{1};
824 char** m_filenames{nullptr};
825
826 template<typename T1, typename T2 = T1>
827 class OptionArg {
828 private:
829 T1* m_return_ptr;
830 T2 m_value;
831 public:
832 OptionArg(T1* ptr, T2 v) : m_return_ptr{ptr}, m_value{v} { }
833 ~OptionArg() { *m_return_ptr = m_value; }
834
835 inline constexpr T2* ptr() noexcept { return &m_value; }
836 };
837
838 using BoolArg = OptionArg<bool, gboolean>;
839 using IntArg = OptionArg<int>;
840 using StrvArg = OptionArg<char**>;
841
842public:
843
844 Options() noexcept = default;
845 Options(Options const&) = delete;
846 Options(Options&&) = delete;
847
848 ~Options() {
849 if (m_filenames != nullptr)
850 g_strfreev(m_filenames);
851 }
852
853 inline constexpr bool benchmark() const noexcept { return m_benchmark; }
854 inline constexpr bool codepoints() const noexcept { return m_codepoints; }
855 inline constexpr bool lint() const noexcept { return m_lint; }
856 inline constexpr bool plain() const noexcept { return m_plain; }
857 inline constexpr bool quiet() const noexcept { return m_quiet; }
858 inline constexpr bool statistics() const noexcept { return m_statistics; }
859 inline constexpr int repeat() const noexcept { return m_repeat; }
860 inline constexpr char const* const* filenames() const noexcept { return m_filenames; }
861
862 bool parse(int argc,
863 char* argv[],
864 GError** error) noexcept
865 {
866 BoolArg benchmark{&m_benchmark, false};
867 BoolArg codepoints{&m_codepoints, false};
868 BoolArg lint{&m_lint, false};
869 BoolArg plain{&m_plain, false};
870 BoolArg quiet{&m_quiet, false};
871 BoolArg statistics{&m_statistics, false};
872 IntArg repeat{&m_repeat, 1};
873 StrvArg filenames{&m_filenames, nullptr};
874 GOptionEntry const entries[] = {
875 { .long_name = "benchmark", .short_name = 'b', .flags = 0, .arg = G_OPTION_ARG_NONE, .arg_data = benchmark.ptr(),
876 .description = "Measure time spent parsing each file", .arg_description = nullptr },
877
878 { .long_name = "codepoints", .short_name = 'u', .flags = 0, .arg = G_OPTION_ARG_NONE, .arg_data = codepoints.ptr(),
879 .description = "Output unicode code points by number", .arg_description = nullptr },
880
881 { .long_name = "lint", .short_name = 'l', .flags = 0, .arg = G_OPTION_ARG_NONE, .arg_data = lint.ptr(),
882 .description = "Check input", .arg_description = nullptr },
883
884 { .long_name = "plain", .short_name = 'p', .flags = 0, .arg = G_OPTION_ARG_NONE, .arg_data = plain.ptr(),
885 .description = "Output plain text without attributes", .arg_description = nullptr },
886
887 { .long_name = "quiet", .short_name = 'q', .flags = 0, .arg = G_OPTION_ARG_NONE, .arg_data = quiet.ptr(),
888 .description = "Suppress output except for statistics and benchmark", .arg_description = nullptr },
889
890 { .long_name = "repeat", .short_name = 'r', .flags = 0, .arg = G_OPTION_ARG_INT, .arg_data = repeat.ptr(),
891 .description = "Repeat each file COUNT times", .arg_description = "COUNT" },
892
893 { .long_name = "statistics", .short_name = 's', .flags = 0, .arg = G_OPTION_ARG_NONE, .arg_data = statistics.ptr(),
894 .description = "Output statistics", .arg_description = nullptr },
895
896 { .long_name = G_OPTION_REMAINING"", .short_name = 0, .flags = 0, .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = filenames.ptr(),
897 .description = nullptr, .arg_description = nullptr },
898 };
899
900 auto context = g_option_context_new("[FILE…] — parser cat");
901 g_option_context_set_help_enabled(context, true);
902 g_option_context_add_main_entries(context, entries, nullptr);
903
904 bool rv = g_option_context_parse(context, &argc, &argv, error);
905 g_option_context_free(context);
906 return rv;
907 }
908}; // class Options
909
910int
911main(int argc,
912 char *argv[])
913{
914 setlocale(LC_ALL6, "");
915 _bte_debug_init_bte_external_debug_init();
916
917 Options options{};
918 auto error = bte::glib::Error{};
919 if (!options.parse(argc, argv, error)) {
1
Assuming the condition is false
2
Taking false branch
920 g_printerr("Failed to parse arguments: %s\n", error.message());
921 return EXIT_FAILURE1;
922 }
923
924 bool rv;
925 Processor proc{};
926 if (options.lint()) {
3
Assuming the condition is true
4
Taking true branch
927 Linter linter{};
928 rv = proc.process_files(options.filenames(), 1, linter);
929 } else if (options.quiet()) {
930 Sink sink{};
931 rv = proc.process_files(options.filenames(), options.repeat(), sink);
932 } else {
933 PrettyPrinter pp{options.plain(), options.codepoints()};
934 rv = proc.process_files(options.filenames(), options.repeat(), pp);
935 }
936
937 if (options.statistics())
5
Assuming the condition is false
6
Taking false branch
938 proc.print_statistics();
939 if (options.benchmark())
7
Assuming the condition is true
8
Taking true branch
940 proc.print_benchmark();
9
Calling 'Processor::print_benchmark'
941
942 return rv ? EXIT_SUCCESS0 : EXIT_FAILURE1;
943}