[vte] parser: Parse subparameters in CSI sequences



commit db833edf98f881085b14ae5240ea65bcff383b91
Author: Christian Persch <chpe src gnome org>
Date:   Tue Mar 27 19:40:12 2018 +0200

    parser: Parse subparameters in CSI sequences
    
    This is used in SGR.

 src/parser-arg.hh  |  112 ++++++++++++++++++++++++++++++++++++++++++++++-----
 src/parser-test.cc |  107 ++++++++++++++++++++++++++++++++++++++------------
 src/parser.cc      |   42 +++++++++++--------
 src/parser.hh      |    1 +
 4 files changed, 208 insertions(+), 54 deletions(-)
---
diff --git a/src/parser-arg.hh b/src/parser-arg.hh
index 75ab3d8..87d9343 100644
--- a/src/parser-arg.hh
+++ b/src/parser-arg.hh
@@ -20,14 +20,65 @@
 
 #include <assert.h>
 
+/*
+ * vte_seq_arg_t:
+ *
+ * A type to hold a CSI, OSC or DCS parameter.
+ *
+ * Parameters can be final or nonfinal.
+ *
+ * Final parameters are those that occur at the end of the
+ * parameter list, or the end of a subparameter list.
+ *
+ * Nonfinal parameters are those that have subparameters
+ * after them.
+ *
+ * Parameters have default value or have a nondefault value.
+ */
 typedef int vte_seq_arg_t;
 
+#define VTE_SEQ_ARG_FLAG_VALUE    (1 << 16)
+#define VTE_SEQ_ARG_FLAG_NONFINAL (1 << 17)
+#define VTE_SEQ_ARG_VALUE_MASK    (0xffff)
+
+/*
+ * VTE_SEQ_ARG_INIT_DEFAULT:
+ *
+ * Returns: a parameter with default value
+ */
 #define VTE_SEQ_ARG_INIT_DEFAULT (0)
-#define VTE_SEQ_ARG_FLAG_VALUE (1 << 16)
-#define VTE_SEQ_ARG_VALUE_MASK (0xffff)
 
-#define VTE_SEQ_ARG_INIT(value) (value | VTE_SEQ_ARG_FLAG_VALUE)
+/*
+ * VTE_SEQ_ARG_INIT:
+ * @value:
+ *
+ * Returns: a parameter with value @value
+ */
+#define VTE_SEQ_ARG_INIT(value) ((value & VTE_SEQ_ARG_VALUE_MASK) | VTE_SEQ_ARG_FLAG_VALUE)
+
+/*
+ * vte_seq_arg_init:
+ * @value:
+ *
+ * Returns: a #vte_seq_arg_t for @value, or with default value if @value is -1
+ */
+static constexpr inline vte_seq_arg_t vte_seq_arg_init(int value)
+{
+        if (value == -1)
+                return VTE_SEQ_ARG_INIT_DEFAULT;
+        else
+                return VTE_SEQ_ARG_INIT(value);
+}
 
+/*
+ * vte_seq_arg_push:
+ * @arg:
+ * @c: a value between 3/0 and 3/9 ['0' .. '9']
+ *
+ * Multiplies @arg by 10 and adds the numeric value of @c.
+ *
+ * After this, @arg has a value.
+ */
 static inline void vte_seq_arg_push(vte_seq_arg_t* arg,
                                     uint32_t c)
 {
@@ -46,26 +97,65 @@ static inline void vte_seq_arg_push(vte_seq_arg_t* arg,
         *arg = value | VTE_SEQ_ARG_FLAG_VALUE;
 }
 
-static inline void vte_seq_arg_finish(vte_seq_arg_t* arg)
+/*
+ * vte_seq_arg_finish:
+ * @arg:
+ * @finalise:
+ *
+ * Finished @arg; after this no more vte_seq_arg_push() calls
+ * are allowed.
+ *
+ * If @nonfinal is %true, marks @arg as a nonfinal parameter, is,
+ * there are more subparameters after it.
+ */
+static inline void vte_seq_arg_finish(vte_seq_arg_t* arg,
+                                      bool nonfinal = false)
 {
+        if (nonfinal)
+                *arg |= VTE_SEQ_ARG_FLAG_NONFINAL;
 }
 
-static inline bool vte_seq_arg_started(vte_seq_arg_t arg)
+/*
+ * vte_seq_arg_started:
+ * @arg:
+ *
+ * Returns: whether @arg has nondefault value
+ */
+static constexpr inline bool vte_seq_arg_started(vte_seq_arg_t arg)
 {
         return arg & VTE_SEQ_ARG_FLAG_VALUE;
 }
 
-static inline bool vte_seq_arg_finished(vte_seq_arg_t arg)
+/*
+ * vte_seq_arg_default:
+ * @arg:
+ *
+ * Returns: whether @arg has default value
+ */
+static constexpr inline bool vte_seq_arg_default(vte_seq_arg_t arg)
 {
-        return true;
+        return !(arg & VTE_SEQ_ARG_FLAG_VALUE);
 }
 
-static inline bool vte_seq_arg_default(vte_seq_arg_t arg)
+/*
+ * vte_seq_arg_nonfinal:
+ * @arg:
+ *
+ * Returns: whether @arg is a nonfinal parameter, i.e. there
+ * are more subparameters after it
+ */
+static constexpr inline int vte_seq_arg_nonfinal(vte_seq_arg_t arg)
 {
-        return !(arg & VTE_SEQ_ARG_FLAG_VALUE);
+        return (arg & VTE_SEQ_ARG_FLAG_NONFINAL);
 }
 
-static inline int vte_seq_arg_value(vte_seq_arg_t arg)
+/*
+ * vte_seq_arg_value:
+ * @arg:
+ *
+ * Returns: the value of @arg, or -1 if @arg has default value
+ */
+static constexpr inline int vte_seq_arg_value(vte_seq_arg_t arg)
 {
-        return arg & VTE_SEQ_ARG_FLAG_VALUE ? arg & VTE_SEQ_ARG_VALUE_MASK : -1;
+        return (arg & VTE_SEQ_ARG_FLAG_VALUE) ? (arg & VTE_SEQ_ARG_VALUE_MASK) : -1;
 }
diff --git a/src/parser-test.cc b/src/parser-test.cc
index 8f84804..eebfa42 100644
--- a/src/parser-test.cc
+++ b/src/parser-test.cc
@@ -23,6 +23,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <string>
+#include <vector>
 
 #include <glib.h>
 
@@ -170,7 +171,8 @@ public:
 
         void set_params(vte_seq_arg_t params[16])
         {
-                memcpy(&m_seq.args, params, 16*sizeof(params[0]));
+                for (unsigned int i = 0; i < 16; i++)
+                        m_seq.args[i] = vte_seq_arg_init(params[i]);
         }
 
         void set_n_params(unsigned int n)
@@ -273,13 +275,9 @@ vte_seq_builder::assert_equal_full(struct vte_seq* seq)
 }
 
 static int
-feed_parser(vte_seq_builder& b,
-            struct vte_seq** seq,
-            bool c1 = false)
+feed_parser(std::u32string const& s,
+            struct vte_seq** seq)
 {
-        std::u32string s;
-        b.to_string(s, c1);
-
         int rv = VTE_SEQ_NONE;
         for (auto it : s) {
                 rv = vte_parser_feed(parser, seq, (uint32_t)(char32_t)it);
@@ -289,6 +287,17 @@ feed_parser(vte_seq_builder& b,
         return rv;
 }
 
+static int
+feed_parser(vte_seq_builder& b,
+            struct vte_seq** seq,
+            bool c1 = false)
+{
+        std::u32string s;
+        b.to_string(s, c1);
+
+        return feed_parser(s, seq);
+}
+
 static void
 test_seq_arg(void)
 {
@@ -302,7 +311,6 @@ test_seq_arg(void)
         vte_seq_arg_push(&arg, '3');
         vte_seq_arg_finish(&arg);
 
-        g_assert_true(vte_seq_arg_finished(arg));
         g_assert_cmpint(vte_seq_arg_value(arg), ==, 123);
         g_assert_false(vte_seq_arg_default(arg));
 
@@ -315,7 +323,6 @@ test_seq_arg(void)
         vte_seq_arg_push(&arg, '6');
         vte_seq_arg_finish(&arg);
 
-        g_assert_true(vte_seq_arg_finished(arg));
         g_assert_cmpint(vte_seq_arg_value(arg), ==, 65535);
 }
 
@@ -776,27 +783,76 @@ test_seq_csi(void)
          * There could be any number of extra params bytes, but we only test up to 1.
          * CSI can be either the C1 control itself, or ESC [
          */
-        vte_seq_arg_t params1[16]{ VTE_SEQ_ARG_INIT(-1), VTE_SEQ_ARG_INIT(0),
-                        VTE_SEQ_ARG_INIT(1), VTE_SEQ_ARG_INIT(9),
-                        VTE_SEQ_ARG_INIT(10), VTE_SEQ_ARG_INIT(99),
-                        VTE_SEQ_ARG_INIT(100), VTE_SEQ_ARG_INIT(999),
-                        VTE_SEQ_ARG_INIT(1000), VTE_SEQ_ARG_INIT(9999),
-                        VTE_SEQ_ARG_INIT(10000), VTE_SEQ_ARG_INIT(65534),
-                        VTE_SEQ_ARG_INIT(65535), VTE_SEQ_ARG_INIT(65536),
-                        VTE_SEQ_ARG_INIT(-1), VTE_SEQ_ARG_INIT(-1) };
+        vte_seq_arg_t params1[16]{ -1, 0, 1, 9, 10, 99, 100, 999,
+                        1000, 9999, 10000, 65534, 65535, 65536, -1, -1 };
         test_seq_csi(params1);
 
-        vte_seq_arg_t params2[16]{ VTE_SEQ_ARG_INIT(1), VTE_SEQ_ARG_INIT(-1),
-                        VTE_SEQ_ARG_INIT(-1), VTE_SEQ_ARG_INIT(-1),
-                        VTE_SEQ_ARG_INIT(1), VTE_SEQ_ARG_INIT(-1),
-                        VTE_SEQ_ARG_INIT(1), VTE_SEQ_ARG_INIT(1),
-                        VTE_SEQ_ARG_INIT(1), VTE_SEQ_ARG_INIT(-1),
-                        VTE_SEQ_ARG_INIT(-1), VTE_SEQ_ARG_INIT(-1),
-                        VTE_SEQ_ARG_INIT(-1), VTE_SEQ_ARG_INIT(1),
-                        VTE_SEQ_ARG_INIT(1), VTE_SEQ_ARG_INIT(1) };
+        vte_seq_arg_t params2[16]{ 1, -1, -1, -1, 1, -1, 1, 1,
+                        1, -1, -1, -1, -1, 1, 1, 1 };
         test_seq_csi(params2);
 }
 
+static void
+test_seq_csi_param(char const* str,
+                   std::vector<int> args,
+                   std::vector<bool> args_nonfinal)
+{
+        g_assert_cmpuint(args.size(), ==, args_nonfinal.size());
+
+        std::u32string s;
+        s.push_back(0x9B); /* CSI */
+        for (unsigned int i = 0; str[i]; i++)
+                s.push_back(str[i]);
+        s.push_back(0x6d); /* m = SGR */
+
+        struct vte_seq* seq;
+        auto rv = feed_parser(s, &seq);
+        g_assert_cmpint(rv, ==, VTE_SEQ_CSI);
+
+        if (seq->n_args < VTE_PARSER_ARG_MAX)
+                g_assert_cmpuint(seq->n_args, ==, args.size());
+
+        unsigned int n_final_args = 0;
+        for (unsigned int i = 0; i < seq->n_args; i++) {
+                g_assert_cmpint(vte_seq_arg_value(seq->args[i]), ==, args[i]);
+
+                auto is_nonfinal = args_nonfinal[i];
+                if (!is_nonfinal)
+                        n_final_args++;
+
+                g_assert_cmpint(!!vte_seq_arg_nonfinal(seq->args[i]), ==, is_nonfinal);
+        }
+
+        g_assert_cmpuint(seq->n_final_args, ==, n_final_args);
+}
+
+static void
+test_seq_csi_param(void)
+{
+        /* Tests that CSI parameters and subparameters are parsed correctly. */
+
+        test_seq_csi_param("", { }, { });
+        test_seq_csi_param(";", { -1, -1 }, { false, false });
+        test_seq_csi_param(":", { -1, -1 }, { true, false });
+        test_seq_csi_param(";:", { -1, -1, -1 }, { false, true, false });
+        test_seq_csi_param("::;;", { -1, -1, -1, -1, -1 }, { true, true, false, false, false });
+
+        test_seq_csi_param("1;2:3:4:5:6;7:8;9:0",
+                           { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 },
+                           { false, true, true, true, true, false, true, false, true, false });
+
+        test_seq_csi_param("1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1",
+                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+                           { false, false, false, false, false, false, false, false,
+                                           false, false, false, false, false, false, false, false });
+
+        test_seq_csi_param("1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+                           { true, true, true, true, true, true, true, true,
+                                           true, true, true, true, true, true, true, false });
+
+}
+
 int
 main(int argc,
      char* argv[])
@@ -818,6 +874,7 @@ main(int argc,
         g_test_add_func("/vte/parser/sequences/escape/nF", test_seq_esc_nF);
         g_test_add_func("/vte/parser/sequences/escape/F[pes]", test_seq_esc_Fpes);
         g_test_add_func("/vte/parser/sequences/csi", test_seq_csi);
+        g_test_add_func("/vte/parser/sequences/csi/parameters", test_seq_csi_param);
 
         auto rv = g_test_run();
 
diff --git a/src/parser.cc b/src/parser.cc
index 5cf74fe..0b2715d 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -619,9 +619,10 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
                          * Split both up and forward the call to the closer
                          * match.
                          */
-                        if (seq->n_args <= 1) /* XTERM RPM */
+                        // FIXMEchpe!
+                        if (seq->n_final_args <= 1) /* XTERM RPM */
                                 return VTE_CMD_XTERM_RPM;
-                        else if (seq->n_args >= 2) /* DECPCTERM */
+                        else if (seq->n_final_args >= 2) /* DECPCTERM */
                                 return VTE_CMD_DECPCTERM;
                 }
                 break;
@@ -659,10 +660,11 @@ static unsigned int vte_parse_host_csi(const struct vte_seq *seq)
                          * 1. We're conservative here and give both a wider
                          * range to allow unused arguments (compat...).
                          */
-                        if (seq->n_args < 5) {
+                        // FIXMEchpe!
+                        if (seq->n_final_args < 5) {
                                 /* SD */
                                 return VTE_CMD_SD;
-                        } else if (seq->n_args >= 5) {
+                        } else if (seq->n_final_args >= 5) {
                                 /* XTERM IHMT */
                                 return VTE_CMD_XTERM_IHMT;
                         }
@@ -931,6 +933,8 @@ static inline void parser_clear(struct vte_parser *parser)
         parser->seq.intermediates = 0;
         parser->seq.charset = VTE_CHARSET_NONE;
         parser->seq.n_args = 0;
+        parser->seq.n_final_args = 0;
+        /* FIXME: we only really need to clear 0..n_args+1 since all others have not been touched */
         for (i = 0; i < VTE_PARSER_ARG_MAX; ++i)
                 parser->seq.args[i] = VTE_SEQ_ARG_INIT_DEFAULT;
 
@@ -990,7 +994,16 @@ static void parser_param(struct vte_parser *parser, uint32_t raw)
 {
         if (raw == ';') {
                 if (parser->seq.n_args < VTE_PARSER_ARG_MAX) {
-                        vte_seq_arg_finish(&parser->seq.args[parser->seq.n_args]);
+                        vte_seq_arg_finish(&parser->seq.args[parser->seq.n_args], false);
+                        ++parser->seq.n_args;
+                        ++parser->seq.n_final_args;
+                }
+
+                return;
+        }
+        if (raw == ':') {
+                if (parser->seq.n_args < VTE_PARSER_ARG_MAX) {
+                        vte_seq_arg_finish(&parser->seq.args[parser->seq.n_args], true);
                         ++parser->seq.n_args;
                 }
 
@@ -1025,8 +1038,9 @@ static int parser_csi(struct vte_parser *parser, uint32_t raw)
         if (parser->seq.n_args < VTE_PARSER_ARG_MAX) {
                 if (parser->seq.n_args > 0 ||
                     vte_seq_arg_started(parser->seq.args[parser->seq.n_args])) {
-                        vte_seq_arg_finish(&parser->seq.args[parser->seq.n_args]);
+                        vte_seq_arg_finish(&parser->seq.args[parser->seq.n_args], false);
                         ++parser->seq.n_args;
+                        ++parser->seq.n_final_args;
                 }
         }
 
@@ -1190,11 +1204,8 @@ static int parser_feed_to_state(struct vte_parser *parser, uint32_t raw)
                 case 0x20 ... 0x2f:        /* [' ' - '\'] */
                         return parser_transition(parser, raw, STATE_CSI_INT,
                                                  ACTION_COLLECT);
-                case 0x3a:                /* ':' */
-                        return parser_transition(parser, raw, STATE_CSI_IGNORE,
-                                                 ACTION_NONE);
                 case 0x30 ... 0x39:        /* ['0' - '9'] */
-                case 0x3b:                /* ';' */
+                case 0x3a ... 0x3b:        /* [':' - ';'] */
                         return parser_transition(parser, raw, STATE_CSI_PARAM,
                                                  ACTION_PARAM);
                 case 0x3c ... 0x3f:        /* ['<' - '?'] */
@@ -1222,10 +1233,9 @@ static int parser_feed_to_state(struct vte_parser *parser, uint32_t raw)
                         return parser_transition(parser, raw, STATE_CSI_INT,
                                                  ACTION_COLLECT);
                 case 0x30 ... 0x39:        /* ['0' - '9'] */
-                case 0x3b:                /* ';' */
+                case 0x3a ... 0x3b:        /* [':' - ';'] */
                         return parser_transition(parser, raw, STATE_NONE,
                                                  ACTION_PARAM);
-                case 0x3a:                /* ':' */
                 case 0x3c ... 0x3f:        /* ['<' - '?'] */
                         return parser_transition(parser, raw, STATE_CSI_IGNORE,
                                                  ACTION_NONE);
@@ -1295,11 +1305,8 @@ static int parser_feed_to_state(struct vte_parser *parser, uint32_t raw)
                 case 0x20 ... 0x2f:        /* [' ' - '\'] */
                         return parser_transition(parser, raw, STATE_DCS_INT,
                                                  ACTION_COLLECT);
-                case 0x3a:                /* ':' */
-                        return parser_transition(parser, raw, STATE_DCS_IGNORE,
-                                                 ACTION_NONE);
                 case 0x30 ... 0x39:        /* ['0' - '9'] */
-                case 0x3b:                /* ';' */
+                case 0x3a ... 0x3b:        /* [':' - ';'] */
                         return parser_transition(parser, raw, STATE_DCS_PARAM,
                                                  ACTION_PARAM);
                 case 0x3c ... 0x3f:        /* ['<' - '?'] */
@@ -1327,10 +1334,9 @@ static int parser_feed_to_state(struct vte_parser *parser, uint32_t raw)
                         return parser_transition(parser, raw, STATE_DCS_INT,
                                                  ACTION_COLLECT);
                 case 0x30 ... 0x39:        /* ['0' - '9'] */
-                case 0x3b:                /* ';' */
+                case 0x3a ... 0x3b:        /* [':' - ';'] */
                         return parser_transition(parser, raw, STATE_NONE,
                                                  ACTION_PARAM);
-                case 0x3a:                /* ':' */
                 case 0x3c ... 0x3f:        /* ['<' - '?'] */
                         return parser_transition(parser, raw, STATE_DCS_IGNORE,
                                                  ACTION_NONE);
diff --git a/src/parser.hh b/src/parser.hh
index 68b440e..1a723af 100644
--- a/src/parser.hh
+++ b/src/parser.hh
@@ -140,6 +140,7 @@ struct vte_seq {
         unsigned int intermediates;
         unsigned int charset;
         unsigned int n_args;
+        unsigned int n_final_args;
         vte_seq_arg_t args[VTE_PARSER_ARG_MAX];
         unsigned int n_st;
         char *st;


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