[vte] parser: cat: Refactor and implement lint mode



commit db83de67084387a73031deaf984b21626879d6be
Author: Christian Persch <chpe src gnome org>
Date:   Sat Oct 6 23:08:28 2018 +0200

    parser: cat: Refactor and implement lint mode
    
    Refactor the parser cat to make it easier plugging in
    a linter instead of a pretty printer, and implement a basic
    linter.

 src/parser-cat.cc | 792 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 466 insertions(+), 326 deletions(-)
---
diff --git a/src/parser-cat.cc b/src/parser-cat.cc
index 8f262a6e..f0b26c54 100644
--- a/src/parser-cat.cc
+++ b/src/parser-cat.cc
@@ -1,5 +1,4 @@
 /*
- * Copyright (C) 2001,2002,2003 Red Hat, Inc.
  * Copyright © 2017, 2018 Christian Persch
  *
  * This programme is free software; you can redistribute it and/or
@@ -29,12 +28,16 @@
 #include <cstdio>
 #include <cstdlib>
 
+#include <string>
+
 #include "debug.h"
 #include "parser.hh"
 #include "utf8.hh"
 
-static char const*
-seq_to_str(unsigned int type)
+using namespace std::literals;
+
+static constexpr char const*
+seq_to_str(unsigned int type) noexcept
 {
         switch (type) {
         case VTE_SEQ_NONE: return "NONE";
@@ -54,8 +57,8 @@ seq_to_str(unsigned int type)
         }
 }
 
-static char const*
-cmd_to_str(unsigned int command)
+static constexpr char const*
+cmd_to_str(unsigned int command) noexcept
 {
         switch (command) {
 #define _VTE_CMD(cmd) case VTE_CMD_##cmd: return #cmd;
@@ -69,8 +72,8 @@ cmd_to_str(unsigned int command)
 }
 
 #if 0
-static char const*
-charset_alias_to_str(unsigned int cs)
+static constexepr char const*
+charset_alias_to_str(unsigned int cs) noexcept
 {
         switch (cs) {
 #define _VTE_CHARSET_PASTE(name)
@@ -87,8 +90,8 @@ charset_alias_to_str(unsigned int cs)
         }
 }
 
-static char const*
-charset_to_str(unsigned int cs)
+static constexpr char const*
+charset_to_str(unsigned int cs) noexcept
 {
         auto alias = charset_alias_to_str(cs);
         if (alias)
@@ -112,312 +115,501 @@ charset_to_str(unsigned int cs)
 }
 #endif
 
-#define SEQ_START "\e[7m"
-#define SEQ_END   "\e[27m"
-
-#define SEQ_START_RED "\e[7;31m"
-#define SEQ_END_RED   "\e[27;39m"
-
-class printer {
-public:
-        printer(GString* str,
-                bool plain,
-                char const* intro,
-                char const* outro)
-                : m_str(str),
-                  m_plain(plain),
-                  m_outro(outro) {
-                if (!m_plain)
-                        g_string_append(m_str, intro);
-        }
-        ~printer() {
-                if (!m_plain)
-                        g_string_append(m_str, m_outro);
-        }
+class PrettyPrinter {
 private:
-        GString* m_str;
+        std::string m_str;
         bool m_plain;
-        char const* m_outro;
-};
+        bool m_codepoints;
+
+        inline constexpr bool plain() const noexcept { return m_plain; }
+
+        class Attribute {
+        public:
+                Attribute(PrettyPrinter* printer,
+                          std::string const& intro,
+                          std::string const& outro) noexcept
+                        : m_printer{printer}
+                        , m_outro{outro} {
+                        if (!m_printer->plain())
+                                m_printer->m_str.append(intro);
+                        }
 
-static void
-print_params(GString* str,
-             struct vte_seq const* seq)
-{
-        if (seq->n_args > 0)
-                g_string_append_c(str, ' ');
-
-        for (unsigned int i = 0; i < seq->n_args; i++) {
-                auto arg = seq->args[i];
-                if (!vte_seq_arg_default(arg))
-                        g_string_append_printf(str, "%d", vte_seq_arg_value(arg));
-                if (i + 1 < seq->n_args)
-                        g_string_append_c(str, vte_seq_arg_nonfinal(arg) ? ':' : ';');
-        }
-}
+                ~Attribute() noexcept
+                {
+                        if (!m_printer->plain())
+                                m_printer->m_str.append(m_outro);
+                }
 
-static void
-print_pintro(GString* str,
-             unsigned int type,
-             unsigned int intermediates)
-{
-        if (type != VTE_SEQ_CSI &&
-            type != VTE_SEQ_DCS)
-                return;
+        private:
+                PrettyPrinter* m_printer;
+                std::string m_outro;
+                bool m_plain;
+        }; // class Attribute
+
+        class ReverseAttr : private Attribute {
+        public:
+                ReverseAttr(PrettyPrinter* printer)
+                        : Attribute(printer, "\e[7m"s, "\e[27m"s)
+                { }
+        };
 
-        unsigned int p = intermediates & 0x7;
-        if (p == 0)
-                return;
+        class RedAttr : private Attribute {
+        public:
+                RedAttr(PrettyPrinter* printer)
+                        : Attribute(printer, "\e[7;31m"s, "\e[27;39m"s)
+                { }
+        };
 
-        g_string_append_c(str, ' ');
-        g_string_append_c(str, 0x40 - p);
-}
+        void
+        print_params(struct vte_seq const* seq) noexcept
+        {
+                if (seq->n_args > 0)
+                        m_str.push_back(' ');
+
+                for (unsigned int i = 0; i < seq->n_args; i++) {
+                        auto arg = seq->args[i];
+                        if (!vte_seq_arg_default(arg))
+                                print_format("%d", vte_seq_arg_value(arg));
+                        if (i + 1 < seq->n_args)
+                                m_str.push_back(vte_seq_arg_nonfinal(arg) ? ':' : ';');
+                }
+        }
 
-static void
-print_intermediates(GString* str,
-                    unsigned int type,
-                    unsigned int intermediates)
-{
-        if (type == VTE_SEQ_CSI ||
-            type == VTE_SEQ_DCS)
-                intermediates = intermediates >> 3; /* remove pintro */
+        void
+        print_pintro(unsigned int type,
+                     unsigned int intermediates) noexcept
+        {
+                if (type != VTE_SEQ_CSI &&
+                    type != VTE_SEQ_DCS)
+                        return;
 
-        while (intermediates != 0) {
-                unsigned int i = intermediates & 0x1f;
-                char c = 0x20 + i - 1;
+                unsigned int p = intermediates & 0x7;
+                if (p == 0)
+                        return;
 
-                g_string_append_c(str, ' ');
-                if (c == 0x20)
-                        g_string_append(str, "SP");
-                else
-                        g_string_append_c(str, c);
+                m_str.push_back(' ');
+                m_str.push_back(char(0x40 - p));
+        }
 
-                intermediates = intermediates >> 5;
+        void
+        print_intermediates(unsigned int type,
+                            unsigned int intermediates) noexcept
+        {
+                if (type == VTE_SEQ_CSI ||
+                    type == VTE_SEQ_DCS)
+                        intermediates = intermediates >> 3; /* remove pintro */
+
+                while (intermediates != 0) {
+                        unsigned int i = intermediates & 0x1f;
+                        char c = 0x20 + i - 1;
+
+                        m_str.push_back(' ');
+                        if (c == 0x20)
+                                m_str.append("SP"s);
+                        else
+                                m_str.push_back(c);
+
+                        intermediates = intermediates >> 5;
+                }
         }
-}
 
-static void
-print_string(GString* str,
-             struct vte_seq const* seq)
-{
-        size_t len;
-        auto buf = vte_seq_string_get(&seq->arg_str, &len);
+        void
+        print_unichar(uint32_t c) noexcept
+        {
+                char buf[7];
+                auto len = g_unichar_to_utf8(c, buf);
+                m_str.append(buf, len);
+        }
 
-        g_string_append_c(str, '\"');
-        for (size_t i = 0; i < len; ++i)
-                g_string_append_unichar(str, buf[i]);
-        g_string_append_c(str, '\"');
-}
+        G_GNUC_PRINTF(2, 3)
+        void
+        print_format(char const* format,
+                     ...)
+        {
+                char buf[256];
+                va_list args;
+                va_start(args, format);
+                auto len = g_vsnprintf(buf, sizeof(buf), format, args);
+                va_end(args);
+
+                m_str.append(buf, len);
+        }
 
-static void
-print_seq_and_params(GString* str,
-                     const struct vte_seq *seq,
-                     bool plain)
-{
-        printer p(str, plain, SEQ_START, SEQ_END);
+        void
+        print_string(struct vte_seq const* seq) noexcept
+        {
+                size_t len;
+                auto buf = vte_seq_string_get(&seq->arg_str, &len);
+                long u8len;
+                auto u8str = g_ucs4_to_utf8(buf, len, nullptr, &u8len, nullptr);
 
-        if (seq->command != VTE_CMD_NONE) {
-                g_string_append_printf(str, "{%s", cmd_to_str(seq->command));
-                print_params(str, seq);
-                g_string_append_c(str, '}');
-        } else {
-                g_string_append_printf(str, "{%s", seq_to_str(seq->type));
-                print_pintro(str, seq->type, seq->intermediates);
-                print_params(str, seq);
-                print_intermediates(str, seq->type, seq->intermediates);
-                g_string_append_printf(str, " %c}", seq->terminator);
-        }
-}
+                m_str.push_back('\"');
+                m_str.append(u8str, size_t(u8len));
+                m_str.push_back('\"');
 
-static void
-print_seq(GString* str,
-          struct vte_seq const* seq,
-          bool codepoints,
-          bool plain)
-{
-        switch (seq->type) {
-        case VTE_SEQ_NONE: {
-                printer p(str, plain, SEQ_START_RED, SEQ_END_RED);
-                g_string_append(str, "{NONE}");
-                break;
+                g_free(u8str);
         }
 
-        case VTE_SEQ_IGNORE: {
-                printer p(str, plain, SEQ_START_RED, SEQ_END_RED);
-                g_string_append(str, "{IGN}");
-                break;
+        void
+        print_seq_and_params(const struct vte_seq *seq) noexcept
+        {
+                ReverseAttr attr(this);
+
+                if (seq->command != VTE_CMD_NONE) {
+                        m_str.push_back('{');
+                        m_str.append(cmd_to_str(seq->command));
+                        print_params(seq);
+                        m_str.push_back('}');
+                } else {
+                        m_str.push_back('{');
+                        m_str.append(seq_to_str(seq->type));
+                        print_pintro(seq->type, seq->intermediates);
+                        print_params(seq);
+                        print_intermediates(seq->type, seq->intermediates);
+                        m_str.push_back(seq->terminator);
+                        m_str.push_back('}');
+                }
         }
 
-        case VTE_SEQ_GRAPHIC: {
-                bool printable = g_unichar_isprint(seq->terminator);
-                if (codepoints || !printable) {
-                        if (printable) {
-                                char ubuf[7];
-                                ubuf[g_unichar_to_utf8(seq->terminator, ubuf)] = 0;
-                                g_string_append_printf(str, "[%04X %s]",
-                                                       seq->terminator, ubuf);
+        void
+        print_seq(struct vte_seq const* seq) noexcept
+        {
+                switch (seq->type) {
+                case VTE_SEQ_NONE: {
+                        RedAttr attr(this);
+                        m_str.append("{NONE}"s);
+                        break;
+                }
+
+                case VTE_SEQ_IGNORE: {
+                        RedAttr attr(this);
+                        m_str.append("{IGNORE}"s);
+                        break;
+                }
+
+                case VTE_SEQ_GRAPHIC: {
+                        bool const printable = g_unichar_isprint(seq->terminator);
+                        if (m_codepoints || !printable) {
+                                if (printable) {
+                                        char ubuf[7];
+                                        ubuf[g_unichar_to_utf8(seq->terminator, ubuf)] = 0;
+                                        print_format("[%04X %s]", seq->terminator, ubuf);
+                                } else {
+                                        print_format("[%04X]", seq->terminator);
+                                }
                         } else {
-                                g_string_append_printf(str, "[%04X]",
-                                                       seq->terminator);
+                                print_unichar(seq->terminator);
                         }
-                } else {
-                        g_string_append_unichar(str, seq->terminator);
+                        break;
                 }
-                break;
-        }
 
-        case VTE_SEQ_CONTROL:
-        case VTE_SEQ_ESCAPE: {
-                printer p(str, plain, SEQ_START, SEQ_END);
-                g_string_append_printf(str, "{%s}", cmd_to_str(seq->command));
-                break;
-        }
+                case VTE_SEQ_CONTROL:
+                case VTE_SEQ_ESCAPE: {
+                        ReverseAttr attr(this);
+                        print_format("{%s}", cmd_to_str(seq->command));
+                        break;
+                }
 
-        case VTE_SEQ_CSI:
-        case VTE_SEQ_DCS: {
-                print_seq_and_params(str, seq, plain);
-                break;
-        }
+                case VTE_SEQ_CSI:
+                case VTE_SEQ_DCS: {
+                        print_seq_and_params(seq);
+                        break;
+                }
+
+                case VTE_SEQ_OSC: {
+                        ReverseAttr attr(this);
+                        m_str.append("{OSC "s);
+                        print_string(seq);
+                        m_str.push_back('}');
+                        break;
+                }
+
+                case VTE_SEQ_SCI: {
+                        if (seq->terminator <= 0x20)
+                                print_format("{SCI %d/%d}",
+                                             seq->terminator / 16,
+                                             seq->terminator % 16);
+                        else
+                                print_format("{SCI %c}", seq->terminator);
+                        break;
+                }
 
-        case VTE_SEQ_OSC: {
-                printer p(str, plain, SEQ_START, SEQ_END);
-                g_string_append(str, "{OSC ");
-                print_string(str, seq);
-                g_string_append_c(str, '}');
-                break;
+                default:
+                        assert(false);
+                }
         }
 
-        case VTE_SEQ_SCI: {
-                if (seq->terminator <= 0x20)
-                  g_string_append_printf(str, "{SCI %d/%d}",
-                                         seq->terminator / 16,
-                                         seq->terminator % 16);
-                else
-                  g_string_append_printf(str, "{SCI %c}", seq->terminator);
-                break;
+        void
+        printout() noexcept
+        {
+                g_print("%s\n", m_str.c_str());
+                m_str.clear();
         }
 
-        default:
-                assert(false);
+public:
+
+        PrettyPrinter(bool plain,
+                      bool codepoints) noexcept
+                : m_plain{plain}
+                , m_codepoints{codepoints}
+        {
         }
-}
 
-static void
-printout(GString* str)
-{
-        g_print("%s\n", str->str);
-        g_string_truncate(str, 0);
-}
+        ~PrettyPrinter() noexcept
+        {
+                printout();
+        }
 
-static gsize seq_stats[VTE_SEQ_N];
-static gsize cmd_stats[VTE_CMD_N];
-static GArray* bench_times;
+        void operator()(struct vte_seq const* seq) noexcept
+        {
+                print_seq(seq);
+                if (seq->command == VTE_CMD_LF)
+                        printout();
+        }
 
-static void
-process_file_utf8(int fd,
-                  bool codepoints,
-                  bool plain,
-                  bool quiet)
-{
-        struct vte_parser parser;
-        vte_parser_init(&parser);
+}; // class PrettyPrinter
 
-        gsize const buf_size = 16384;
-        guchar* buf = g_new0(guchar, buf_size);
-        auto outbuf = g_string_sized_new(buf_size);
+class Linter {
+private:
+        void
+        warn(char const* str) const noexcept
+        {
+                g_printerr("WARNING: %s\n", str);
+        }
 
-        auto start_time = g_get_monotonic_time();
+        void
+        warn_deprecated(int cmd,
+                        int replacement_cmd) const noexcept
+        {
+                g_printerr("WARNING: %s is deprecated; use %s instead.\n",
+                           cmd_to_str(cmd),
+                           cmd_to_str(replacement_cmd));
+        }
 
-        vte::base::UTF8Decoder decoder;
+public:
+        constexpr Linter() noexcept = default;
+        ~Linter() noexcept = default;
+
+        void operator()(struct vte_seq const* seq) noexcept
+        {
+                auto const cmd = seq->command;
+                switch (cmd) {
+                case VTE_CMD_OSC:
+                        if (seq->terminator == 7 /* BEL */)
+                                warn("OSC terminated by BEL may be ignored; use ST (ESC \\) instead.");
+                        break;
 
-        gsize buf_start = 0;
-        for (;;) {
-                auto len = read(fd, buf + buf_start, buf_size - buf_start);
-                if (!len)
+                case VTE_CMD_DECSLRM_OR_SCOSC:
+                case VTE_CMD_SCOSC:
+                        warn_deprecated(VTE_CMD_SCOSC, VTE_CMD_DECSC);
                         break;
-                if (len == -1) {
-                        if (errno == EAGAIN)
-                                continue;
+
+                case VTE_CMD_SCORC:
+                        warn_deprecated(cmd, VTE_CMD_DECRC);
                         break;
                 }
+        }
 
-                auto const bufend = buf + len;
-
-                struct vte_seq *seq = &parser.seq;
-
-                for (auto sptr = buf; sptr < bufend; ++sptr) {
-                        switch (decoder.decode(*sptr)) {
-                        case vte::base::UTF8Decoder::REJECT_REWIND:
-                                /* Rewind the stream.
-                                 * Note that this will never lead to a loop, since in the
-                                 * next round this byte *will* be consumed.
-                                 */
-                                --sptr;
-                                [[fallthrough]];
-                        case vte::base::UTF8Decoder::REJECT:
-                                decoder.reset();
-                                /* Fall through to insert the U+FFFD replacement character. */
-                                [[fallthrough]];
-                        case vte::base::UTF8Decoder::ACCEPT: {
-                                auto ret = vte_parser_feed(&parser, decoder.codepoint());
-                                if (G_UNLIKELY(ret < 0)) {
-                                        g_printerr("Parser error!\n");
-                                        goto out;
-                                }
+}; // class Linter
+
+class Sink {
+public:
+        void operator()(struct vte_seq const* seq) noexcept { }
+
+}; // class Sink
+
+class Processor {
+private:
+        gsize m_seq_stats[VTE_SEQ_N];
+        gsize m_cmd_stats[VTE_CMD_N];
+        GArray* m_bench_times;
+
+        template <class Functor>
+        void
+        process_file_utf8(int fd,
+                          Functor& func)
+        {
+                struct vte_parser parser;
+                vte_parser_init(&parser);
+
+                gsize const buf_size = 16384;
+                guchar* buf = g_new0(guchar, buf_size);
+
+                auto start_time = g_get_monotonic_time();
+
+                vte::base::UTF8Decoder decoder;
+
+                gsize buf_start = 0;
+                for (;;) {
+                        auto len = read(fd, buf + buf_start, buf_size - buf_start);
+                        if (!len)
+                                break;
+                        if (len == -1) {
+                                if (errno == EAGAIN)
+                                        continue;
+                                break;
+                        }
+
+                        auto const bufend = buf + len;
+
+                        struct vte_seq *seq = &parser.seq;
+
+                        for (auto sptr = buf; sptr < bufend; ++sptr) {
+                                switch (decoder.decode(*sptr)) {
+                                case vte::base::UTF8Decoder::REJECT_REWIND:
+                                        /* Rewind the stream.
+                                         * Note that this will never lead to a loop, since in the
+                                         * next round this byte *will* be consumed.
+                                         */
+                                        --sptr;
+                                        [[fallthrough]];
+                                case vte::base::UTF8Decoder::REJECT:
+                                        decoder.reset();
+                                        /* Fall through to insert the U+FFFD replacement character. */
+                                        [[fallthrough]];
+                                case vte::base::UTF8Decoder::ACCEPT: {
+                                        auto ret = vte_parser_feed(&parser, decoder.codepoint());
+                                        if (G_UNLIKELY(ret < 0)) {
+                                                g_printerr("Parser error!\n");
+                                                goto out;
+                                        }
 
-                                seq_stats[ret]++;
-                                if (ret != VTE_SEQ_NONE) {
-                                        cmd_stats[seq->command]++;
-                                        if (!quiet) {
-                                                print_seq(outbuf, seq, codepoints, plain);
-                                                if (seq->command == VTE_CMD_LF)
-                                                        printout(outbuf);
+                                        m_seq_stats[ret]++;
+                                        if (ret != VTE_SEQ_NONE) {
+                                                m_cmd_stats[seq->command]++;
+                                                func(seq);
                                         }
+                                        break;
+                                }
+
+                                default:
+                                        break;
                                 }
-                                break;
                         }
+                }
 
-                        default:
-                                break;
+        out:
+
+                int64_t time_spent = g_get_monotonic_time() - start_time;
+                g_array_append_val(m_bench_times, time_spent);
+
+                g_free(buf);
+                vte_parser_deinit(&parser);
+        }
+
+        template <class Functor>
+        bool
+        process_file(int fd,
+                     int repeat,
+                     Functor& func)
+        {
+                if (fd == STDIN_FILENO && repeat != 1) {
+                        g_printerr("Cannot consume STDIN more than once\n");
+                        return false;
+                }
+
+                for (auto i = 0; i < repeat; ++i) {
+                        if (i > 0 && lseek(fd, 0, SEEK_SET) != 0) {
+                                g_printerr("Failed to seek: %m\n");
+                                return false;
                         }
+
+                        process_file_utf8(fd, func);
                 }
+
+                return true;
         }
 
- out:
-        if (!quiet)
-                printout(outbuf);
+public:
 
-        int64_t time_spent = g_get_monotonic_time() - start_time;
-        g_array_append_val(bench_times, time_spent);
+        Processor() noexcept
+        {
+                memset(&m_seq_stats, 0, sizeof(m_seq_stats));
+                memset(&m_cmd_stats, 0, sizeof(m_cmd_stats));
+                m_bench_times = g_array_new(false, true, sizeof(int64_t));
+        }
 
-        g_string_free(outbuf, TRUE);
-        g_free(buf);
-        vte_parser_deinit(&parser);
-}
+        ~Processor() noexcept
+        {
+                g_array_free(m_bench_times, true);
+        }
 
-static bool
-process_file(int fd,
-             bool codepoints,
-             bool plain,
-             bool quiet,
-             int repeat)
-{
-        if (fd == STDIN_FILENO && repeat != 1) {
-                g_printerr("Cannot consume STDIN more than once\n");
-                return false;
+        template <class Functor>
+        bool
+        process_files(char const* const* filenames,
+                      int repeat,
+                      Functor& func)
+        {
+                bool r = true;
+                if (filenames != nullptr) {
+                        for (auto i = 0; filenames[i] != nullptr; i++) {
+                                char const* filename = filenames[i];
+
+                                int fd = -1;
+                                if (g_str_equal(filename, "-")) {
+                                        fd = STDIN_FILENO;
+                                } else {
+                                        fd = open(filename, O_RDONLY);
+                                        if (fd == -1) {
+                                                g_printerr("Error opening file %s: %m\n", filename);
+                                        }
+                                }
+                                if (fd != -1) {
+                                        r = process_file(fd, repeat, func);
+                                        close(fd);
+                                        if (!r)
+                                                break;
+                                }
+                        }
+                } else {
+                        r = process_file(STDIN_FILENO, repeat, func);
+                }
+
+                return r;
         }
 
-        for (auto i = 0; i < repeat; ++i) {
-                if (i > 0 && lseek(fd, 0, SEEK_SET) != 0) {
-                        g_printerr("Failed to seek: %m\n");
-                        return false;
+        void print_statistics() const noexcept
+        {
+                for (unsigned int s = VTE_SEQ_NONE + 1; s < VTE_SEQ_N; s++) {
+                        g_printerr("%\'16" G_GSIZE_FORMAT " %s\n",  m_seq_stats[s], seq_to_str(s));
                 }
 
-                process_file_utf8(fd, codepoints, plain, quiet);
+                g_printerr("\n");
+                for (unsigned int s = 0; s < VTE_CMD_N; s++) {
+                        if (m_cmd_stats[s] > 0) {
+                                g_printerr("%\'16" G_GSIZE_FORMAT " %s%s\n",
+                                           m_cmd_stats[s],
+                                           cmd_to_str(s),
+                                           s >= VTE_CMD_NOP_FIRST ? " [NOP]" : "");
+                        }
+                }
         }
 
-        return true;
-}
+        void print_benchmark() const noexcept
+        {
+                g_array_sort(m_bench_times,
+                             [](void const* p1, void const* p2) -> int {
+                                     int64_t const t1 = *(int64_t const*)p1;
+                                     int64_t const t2 = *(int64_t const*)p2;
+                                     return t1 == t2 ? 0 : (t1 < t2 ? -1 : 1);
+                             });
+
+                int64_t total_time = 0;
+                for (unsigned int i = 0; i < m_bench_times->len; ++i)
+                        total_time += g_array_index(m_bench_times, int64_t, i);
+
+                g_printerr("\nTimes: best %\'" G_GINT64_FORMAT "µs "
+                           "worst %\'" G_GINT64_FORMAT "µs "
+                           "average %\'" G_GINT64_FORMAT "µs\n",
+                           g_array_index(m_bench_times, int64_t, 0),
+                           g_array_index(m_bench_times, int64_t, m_bench_times->len - 1),
+                           total_time / (int64_t)m_bench_times->len);
+                for (unsigned int i = 0; i < m_bench_times->len; ++i)
+                        g_printerr("  %\'" G_GINT64_FORMAT "µs\n",
+                                   g_array_index(m_bench_times, int64_t, i));
+        }
+
+}; // class Processor
 
 int
 main(int argc,
@@ -425,6 +617,7 @@ main(int argc,
 {
         gboolean benchmark = false;
         gboolean codepoints = false;
+        gboolean lint = false;
         gboolean plain = false;
         gboolean quiet = false;
         gboolean statistics = false;
@@ -435,6 +628,8 @@ main(int argc,
                   "Measure time spent parsing each file", nullptr },
                 { "codepoints", 'u', 0, G_OPTION_ARG_NONE, &codepoints,
                   "Output unicode code points by number", nullptr },
+                { "lint", 'l', 0, G_OPTION_ARG_NONE, &lint,
+                  "Check input", nullptr },
                 { "plain", 'p', 0, G_OPTION_ARG_NONE, &plain,
                   "Output plain text without attributes", nullptr },
                 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,
@@ -465,80 +660,25 @@ main(int argc,
                 return EXIT_FAILURE;
         }
 
-        int exit_status = EXIT_FAILURE;
-
-        memset(&seq_stats, 0, sizeof(seq_stats));
-        memset(&cmd_stats, 0, sizeof(cmd_stats));
-        bench_times = g_array_new(false, true, sizeof(int64_t));
-
-        if (filenames != nullptr) {
-                for (auto i = 0; filenames[i] != nullptr; i++) {
-                        char const* filename = filenames[i];
-
-                        int fd = -1;
-                        if (g_str_equal(filename, "-")) {
-                                fd = STDIN_FILENO;
-                        } else {
-                                fd = open(filename, O_RDONLY);
-                                if (fd == -1) {
-                                        g_printerr("Error opening file %s: %m\n", filename);
-                                }
-                        }
-                        if (fd != -1) {
-                                bool r = process_file(fd, codepoints, plain, quiet, repeat);
-                                close(fd);
-                                if (!r)
-                                        break;
-                        }
-                }
-
-                g_strfreev(filenames);
-                exit_status = EXIT_SUCCESS;
+        Processor proc{};
+        if (lint) {
+                Linter linter{};
+                rv = proc.process_files(filenames, 1, linter);
+        } else if (quiet) {
+                Sink sink{};
+                rv = proc.process_files(filenames, repeat, sink);
         } else {
-                if (process_file(STDIN_FILENO, codepoints, plain, quiet, repeat))
-                        exit_status = EXIT_SUCCESS;
-        }
-
-        if (statistics) {
-                for (unsigned int s = VTE_SEQ_NONE + 1; s < VTE_SEQ_N; s++) {
-                        g_printerr("%\'16" G_GSIZE_FORMAT " %s\n",  seq_stats[s], seq_to_str(s));
-                }
-
-                g_printerr("\n");
-                for (unsigned int s = 0; s < VTE_CMD_N; s++) {
-                        if (cmd_stats[s] > 0) {
-                                g_printerr("%\'16" G_GSIZE_FORMAT " %s%s\n",
-                                           cmd_stats[s],
-                                           cmd_to_str(s),
-                                           s >= VTE_CMD_NOP_FIRST ? " [NOP]" : "");
-                        }
-                }
+                PrettyPrinter pp{plain, codepoints};
+                rv = proc.process_files(filenames, repeat, pp);
         }
 
-        if (benchmark) {
-                g_array_sort(bench_times,
-                             [](void const* p1, void const* p2) -> int {
-                                     int64_t const t1 = *(int64_t const*)p1;
-                                     int64_t const t2 = *(int64_t const*)p2;
-                                     return t1 == t2 ? 0 : (t1 < t2 ? -1 : 1);
-                             });
-
-                int64_t total_time = 0;
-                for (unsigned int i = 0; i < bench_times->len; ++i)
-                        total_time += g_array_index(bench_times, int64_t, i);
+        if (statistics)
+                proc.print_statistics();
+        if (benchmark)
+                proc.print_benchmark();
 
-                g_printerr("\nTimes: best %\'" G_GINT64_FORMAT "µs "
-                           "worst %\'" G_GINT64_FORMAT "µs "
-                           "average %\'" G_GINT64_FORMAT "µs\n",
-                           g_array_index(bench_times, int64_t, 0),
-                           g_array_index(bench_times, int64_t, bench_times->len - 1),
-                           total_time / (int64_t)bench_times->len);
-                for (unsigned int i = 0; i < bench_times->len; ++i)
-                        g_printerr("  %\'" G_GINT64_FORMAT "µs\n",
-                                   g_array_index(bench_times, int64_t, i));
-        }
-
-        g_array_free(bench_times,true);
+        if (filenames != nullptr)
+                g_strfreev(filenames);
 
-        return exit_status;
+        return rv ? EXIT_SUCCESS : EXIT_FAILURE;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]