]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
wip! Add ply-terminal-buffer object plymouth-terminal-buffer
authorRay Strode <rstrode@redhat.com>
Fri, 5 May 2023 20:22:11 +0000 (16:22 -0400)
committerRay Strode <rstrode@redhat.com>
Fri, 5 May 2023 20:33:30 +0000 (16:33 -0400)
We're going to need to do at least basic parsing of terminal text
so we can show the console to the user in color when they hit
escape and VTs are disabled.

This commit adds the start of a class to do this basic parsing.

The way it works is the terminal text is inject into the object
and all escape sequences are filtered out. When a color control
sequence is found, the current color is noted, along with which
characters it applies to. A list of the text spans and their
color attributes is then iteratable using a an api.

src/libply-splash-core/meson.build
src/libply-splash-core/ply-terminal-buffer.c [new file with mode: 0644]
src/libply-splash-core/ply-terminal-buffer.h [new file with mode: 0644]

index 69636b1338393562fd90af9c3034bdbf9c14e07e..0fa7102b468a4f9679ba2ffd3ad865d477974305 100644 (file)
@@ -7,6 +7,7 @@ libply_splash_core_sources = files(
   'ply-pixel-display.c',
   'ply-renderer.c',
   'ply-terminal.c',
+  'ply-terminal-buffer.c',
   'ply-text-display.c',
   'ply-text-progress-bar.c',
   'ply-text-step-bar.c',
@@ -60,6 +61,7 @@ libply_splash_core_headers = files(
   'ply-renderer-plugin.h',
   'ply-renderer.h',
   'ply-terminal.h',
+  'ply-terminal-buffer.h',
   'ply-text-display.h',
   'ply-text-progress-bar.h',
   'ply-text-step-bar.h',
diff --git a/src/libply-splash-core/ply-terminal-buffer.c b/src/libply-splash-core/ply-terminal-buffer.c
new file mode 100644 (file)
index 0000000..729a3b7
--- /dev/null
@@ -0,0 +1,174 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define PLY_TERMINAL_COLOR_ATTRIBUTE_OFFSET 30
+
+typedef struct
+{
+        ply_terminal_color_t color;
+        size_t               start_index;
+        size_t               span_length;
+} ply_text_attribute_t;
+
+struct _ply_terminal_buffer
+{
+        ply_buffer_t *text_buffer;
+        ply_list_t   *attributes;
+};
+
+typedef enum
+{
+        PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED,
+        PLY_TERMINAL_BUFFER_PARSER_STATE_ESCAPED,
+        PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE,
+        PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER
+} ply_terminal_buffer_parser_state_t;
+
+
+ply_terminal_buffer_t *
+ply_terminal_buffer_new (void)
+{
+        ply_terminal_buffer_t *terminal_buffer = calloc (1, sizeof(ply_terminal_buffer_t));
+
+        terminal_buffer->text_buffer = ply_buffer_new ();
+
+        return buffer;
+}
+
+void
+ply_terminal_buffer_free (ply_terminal_buffer_t *terminal_buffer)
+{
+        ply_list_node_t *node;
+
+        ply_buffer_free (terminal_buffer->text_buffer);
+        ply_list_foreach (terminal_buffer->attributes, node) {
+                ply_text_attribute_t *attribute;
+                attribute = ply_list_node_get_data (node);
+                free (attribute);
+        }
+        ply_list_free (terminal_buffer->attributes);
+        free (terminal_buffer);
+}
+
+void
+ply_terminal_buffer_inject (ply_terminal_buffer_t *buffer,
+                            const char            *input,
+                            size_t                 length)
+{
+        ply_terminal_buffer_parser_state_t state = PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED;
+        ply_terminal_color_t color = PLY_TERMINAL_COLOR_WHITE;
+        size_t i = 0, end_index;
+        size_t parameter_start = 0;
+
+        start_index = ply_buffer_get_size (buffer->text_buffer);
+        end_index = start_index;
+
+        while (i < length) {
+                switch (state) {
+                case PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED:
+                        if (input[i] == '\033') {
+                                state = PLY_TERMINAL_BUFFER_PARSER_STATE_ESCAPED;
+                        } else {
+                                ply_buffer_append (buffer->text_buffer, "%c", input[i]);
+                                end_index++;
+                        }
+                        break;
+                case PLY_TERMINAL_BUFFER_PARSER_STATE_ESCAPED:
+                        if (input[i] == '[') {
+                                state = PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE;
+                        }
+                        break;
+                case PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE:
+                        if (isdigit (input[i])) {
+                                parameter_start = i;
+                                state = PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER;
+                        }
+                        break;
+                case PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER:
+                        if (input[i] == ';' || input[i] == 'm') {
+                                int parameter_value;
+
+                                parameter_value = atoi (&input[parameter_start]) - PLY_TERMINAL_COLOR_ATTRIBUTE_OFFSET;
+                                if (parameter_value >= PLY_TERMINAL_COLOR_BLACK && parameter_value <= PLY_TERMINAL_COLOR_WHITE) {
+                                        color = (ply_terminal_color_t) (parameter_value);
+                                }
+                        }
+
+                        if (input[i] == ';') {
+                                parameter_start = i + 1;
+                        } else if (input[i] == 'm') {
+                                if (end_index > start_index) {
+                                        ply_text_attribute_t *attribute;
+
+                                        attribute = calloc (1, sizeof(ply_text_attribute_t));
+                                        attribute->color = color;
+                                        attribute->start_index = start_index;
+                                        attribute->span_length = end_index - start_index;
+
+                                        ply_list_append_data (buffer->attributes, attribute);
+                                }
+                                state = PLY_TERMINAL_BUFFER_PARSER_STATE_UNESCAPED;
+                        }
+                        break;
+                }
+                i++;
+        }
+
+        if (state == PLY_TERMINAL_BUFFER_PARSER_STATE_CONTROL_SEQUENCE_PARAMETER &&
+            end_index > start_index) {
+                ply_text_attribute_t *attribute;
+
+                attribute = calloc (1, sizeof(ply_text_attribute_t));
+                attribute->color = color;
+                attribute->start_index = start_index;
+                attribute->span_length = end_index - start_index;
+
+                ply_list_append_data (buffer->attributes, attribute);
+        }
+}
+
+void
+ply_terminal_buffer_iter_init (ply_terminal_buffer_iter_t *iter,
+                               ply_terminal_buffer_t      *buffer)
+{
+        iter->buffer = buffer;
+        iter->node = ply_list_get_First_node (buffer->attributes);
+}
+
+int
+ply_terminal_buffer_iter_next (ply_terminal_buffer_iter_t *iter,
+                               ply_text_attribute_t      **attribute,
+                               const char                **text,
+                               size_t                     *start_index,
+                               size_t                     *end_index)
+{
+        ply_terminal_buffer_t *buffer;
+        ply_text_attribute_t *current_attribute, *next_attribute;
+        const char *bytes;
+
+        if (iter->node == NULL) {
+                return false;
+        }
+
+        buffer = iter->buffer;
+        bytes = ply_terminal_buffer_get_bytes (buffer);
+
+        current_attribute = ply_list_node_get_data (iter->node);
+        *text = bytes + current_attribute->start_index;
+        *attribute = current_attribute;
+        *start_index = current_attribute->start_index;
+
+        iter->node = ply_list_get_next_node (buffer->attributes, iter->node);
+
+        if (iter->node != NULL) {
+                next_attribute = ply_list_node_get_data (iter->node);
+                *end_index = next_attribute->start_index - 1;
+        } else {
+                size_t last_index = ply_buffer_get_size (buffer) - 1;
+                *end_index = last_index;
+        }
+
+        return true;
+}
diff --git a/src/libply-splash-core/ply-terminal-buffer.h b/src/libply-splash-core/ply-terminal-buffer.h
new file mode 100644 (file)
index 0000000..37ac4d0
--- /dev/null
@@ -0,0 +1,58 @@
+/* ply-terminal-buffer.h - APIs for handling terminal text
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+#ifndef PLY_TERMINAL_BUFFER_H
+#define PLY_TERMINAL_BUFFER_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "ply-buffer.h"
+#include "ply-event-loop.h"
+
+typedef struct _ply_terminal_buffer ply_terminal_buffer_t;
+
+typedef struct
+{
+        ply_terminal_buffer_t *buffer;
+        ply_list_node_t       *node;
+} ply_terminal_buffer_iter_t;
+
+#ifndef PLY_HIDE_FUNCTION_DECLARATIONS
+ply_terminal_buffer_t *ply_terminal_buffer_new (void) :
+        void ply_terminal_buffer_free (ply_terminal_buffer_t *terminal_buffer);
+
+void ply_terminal_buffer_inject (ply_terminal_buffer_t *buffer,
+                                 const char            *input,
+                                 size_t                 length);
+void ply_terminal_buffer_iter_init (ply_terminal_buffer_iter_t *iter,
+                                    ply_terminal_buffer_t      *buffer);
+int ply_terminal_buffer_iter_next (ply_terminal_buffer_iter_t *iter,
+                                   ply_text_attribute_t      **attribute,
+                                   const char                **text,
+                                   size_t                     *start_index,
+                                   size_t                     *end_index);
+
+#endif
+
+#endif /* PLY_TERMINAL_BUFFER_H */