Commit c4cd94d2 authored by Christian Persch's avatar Christian Persch

parser: Implement OSC parsing

parent e9fa511f
......@@ -65,6 +65,8 @@ libvte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_la_SOURCES = \
parser-charset-tables.hh \
parser-cmd.hh \
parser-glue.hh \
parser-osc.hh \
parser-string.hh \
pty.cc \
reaper.cc \
reaper.hh \
......@@ -248,6 +250,8 @@ parser_cat_SOURCES = \
parser-charset-tables.hh \
parser-cmd.hh \
parser-glue.hh \
parser-osc.hh \
parser-string.hh \
parser-cat.cc \
vteconv.cc \
vteconv.h \
......@@ -281,6 +285,8 @@ test_parser_SOURCES = \
parser-charset.hh \
parser-charset-tables.hh \
parser-glue.hh \
parser-osc.hh \
parser-string.hh \
$(NULL)
test_parser_CPPFLAGS = \
-I$(builddir) \
......
......@@ -103,7 +103,7 @@ static inline void vte_seq_arg_push(vte_seq_arg_t* arg,
* @arg:
* @finalise:
*
* Finished @arg; after this no more vte_seq_arg_push() calls
* Finishes @arg; after this no more vte_seq_arg_push() calls
* are allowed.
*
* If @nonfinal is %true, marks @arg as a nonfinal parameter, is,
......
......@@ -168,6 +168,19 @@ print_intermediates(GString* str,
return any;
}
static void
print_string(GString* str,
struct vte_seq const* seq)
{
size_t len;
auto buf = vte_seq_string_get(&seq->arg_str, &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, '\"');
}
static void
print_seq_and_params(GString* str,
const struct vte_seq *seq,
......@@ -238,12 +251,19 @@ print_seq(GString* str,
}
case VTE_SEQ_CSI:
case VTE_SEQ_DCS:
case VTE_SEQ_OSC: {
case VTE_SEQ_DCS: {
print_seq_and_params(str, seq, plain);
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);
}
......
......@@ -152,6 +152,7 @@ _VTE_CMD(MC_DEC) /* media-copy-dec */
_VTE_CMD(NEL) /* next-line */
_VTE_CMD(NP) /* next-page */
_VTE_CMD(NUL) /* nul */
_VTE_CMD(OSC) /* operating-system-command */
_VTE_CMD(PP) /* preceding-page */
_VTE_CMD(PPA) /* page-position-absolute */
_VTE_CMD(PPB) /* page-position-backward */
......
......@@ -19,6 +19,7 @@
#include <cstdint>
#include <algorithm>
#include <string>
#include "parser.hh"
......@@ -31,7 +32,8 @@ public:
typedef int number;
char* ucs4_to_utf8(gunichar const* str) const noexcept;
char* ucs4_to_utf8(gunichar const* str,
ssize_t len = -1) const noexcept;
void print() const noexcept;
......@@ -91,6 +93,53 @@ public:
return m_seq->terminator;
}
// FIXMEchpe: upgrade to C++17 and use the u32string_view version below, instead
/*
* string:
*
* This is the string argument of a DCS or OSC sequence.
*
* Returns: the string argument
*/
inline std::u32string string() const noexcept
{
size_t len;
auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
return std::u32string(reinterpret_cast<char32_t*>(buf), len);
}
#if 0
/*
* string:
*
* This is the string argument of a DCS or OSC sequence.
*
* Returns: the string argument
*/
inline constexpr std::u32string_view string() const noexcept
{
size_t len = 0;
auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
return std::u32string_view(buf, len);
}
#endif
/*
* string:
*
* This is the string argument of a DCS or OSC sequence.
*
* Returns: the string argument
*/
std::string string_utf8() const noexcept;
inline char* string_param() const noexcept
{
size_t len = 0;
auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
return ucs4_to_utf8(buf, len);
}
/* size:
*
* Returns: the number of parameters
......@@ -274,105 +323,232 @@ public:
return idx <= next(start_idx);
}
//FIMXE remove this one
inline constexpr int operator[](int position) const
{
return __builtin_expect(position < (int)size(), 1) ? vte_seq_arg_value(m_seq->args[position]) : -1;
}
struct vte_seq** seq_ptr() { return &m_seq; }
inline explicit operator bool() const { return m_seq != nullptr; }
private:
struct vte_seq *m_seq{nullptr};
inline bool has_number_at_unchecked(unsigned int position) const
char const* type_string() const;
char const* command_string() const;
};
class StringTokeniser {
public:
using string_type = std::string;
using char_type = std::string::value_type;
private:
string_type const& m_string;
char_type m_separator{';'};
public:
StringTokeniser(string_type const& s,
char_type separator = ';')
: m_string{s},
m_separator{separator}
{
return true;
}
inline bool number_at_unchecked(unsigned int position, number& v) const
StringTokeniser(string_type&& s,
char_type separator = ';')
: m_string{s},
m_separator{separator}
{
v = vte_seq_arg_value(m_seq->args[position]);
return true;
}
inline bool number_at(unsigned int position, number& v) const
{
if (G_UNLIKELY(position >= size()))
return false;
StringTokeniser(StringTokeniser const&) = delete;
StringTokeniser(StringTokeniser&&) = delete;
~StringTokeniser() = default;
return number_at_unchecked(position, v);
}
StringTokeniser& operator=(StringTokeniser const&) = delete;
StringTokeniser& operator=(StringTokeniser&&) = delete;
inline number number_or_default_at_unchecked(unsigned int position, number default_v = 0) const
{
number v;
if (G_UNLIKELY(!number_at_unchecked(position, v)))
v = default_v;
return v;
}
/*
* const_iterator:
*
* InputIterator for string tokens.
*/
class const_iterator {
public:
using difference_type = ptrdiff_t;
using value_type = string_type;
using pointer = string_type;
using reference = string_type;
using iterator_category = std::input_iterator_tag;
using size_type = string_type::size_type;
private:
string_type const* m_string;
char_type m_separator{';'};
string_type::size_type m_position;
string_type::size_type m_next_separator;
public:
const_iterator(string_type const* str,
char_type separator,
size_type position)
: m_string{str},
m_separator{separator},
m_position{position},
m_next_separator{m_string->find(m_separator, m_position)}
{
}
const_iterator(string_type const* str,
char_type separator)
: m_string{str},
m_separator{separator},
m_position{string_type::npos},
m_next_separator{string_type::npos}
{
}
inline number number_or_default_at(unsigned int position, number default_v = 0) const
{
number v;
if (G_UNLIKELY(!number_at(position, v)))
v = default_v;
return v;
}
const_iterator(const_iterator const&) = default;
const_iterator(const_iterator&& o)
: m_string{o.m_string},
m_separator{o.m_separator},
m_position{o.m_position},
m_next_separator{o.m_next_separator}
{
}
inline bool string_at_unchecked(unsigned int position, char*& str) const
{
#if 0
auto value = value_at_unchecked(position);
if (G_LIKELY(G_VALUE_HOLDS_POINTER(value))) {
str = ucs4_to_utf8((gunichar const*)g_value_get_pointer (value));
return str != nullptr;
~const_iterator() = default;
const_iterator& operator=(const_iterator const& o)
{
m_string = o.m_string;
m_separator = o.m_separator;
m_position = o.m_position;
m_next_separator = o.m_next_separator;
return *this;
}
if (G_VALUE_HOLDS_STRING(value)) {
/* Copy the string into the buffer. */
str = g_value_dup_string(value);
return str != nullptr;
const_iterator& operator=(const_iterator&& o)
{
m_string = std::move(o.m_string);
m_separator = o.m_separator;
m_position = o.m_position;
m_next_separator = o.m_next_separator;
return *this;
}
if (G_VALUE_HOLDS_LONG(value)) {
/* Convert the long to a string. */
str = g_strdup_printf("%ld", g_value_get_long(value));
return true;
inline bool operator==(const_iterator const& o) const noexcept
{
return m_position == o.m_position;
}
inline bool operator!=(const_iterator const& o) const noexcept
{
return m_position != o.m_position;
}
inline const_iterator& operator++() noexcept
{
if (m_next_separator != string_type::npos) {
m_position = ++m_next_separator;
m_next_separator = m_string->find(m_separator, m_position);
} else
m_position = string_type::npos;
return *this;
}
/*
* number:
*
* Returns the value of the iterator as a number, or -1
* if the string could not be parsed as a number, or
* the parsed values exceeds the uint16_t range.
*
* Returns: true if a number was parsed
*/
bool number(int& v) const noexcept
{
auto const s = size();
if (s == 0) {
v = -1;
return true;
}
v = 0;
size_type i;
for (i = 0; i < s; ++i) {
char_type c = (*m_string)[m_position + i];
if (c < '0' || c > '9')
return false;
v = v * 10 + (c - '0');
if (v > 0xffff)
return false;
}
/* All consumed? */
return i == s;
}
inline size_type size() const noexcept
{
if (m_next_separator != string_type::npos)
return m_next_separator - m_position;
else
return m_string->size() - m_position;
}
inline size_type size_remaining() const noexcept
{
return m_string->size() - m_position;
}
#endif
str = nullptr;
return false;
}
inline bool string_at(unsigned int position, char*& str) const
inline string_type operator*() const noexcept
{
return m_string->substr(m_position, size());
}
/*
* string_remaining:
*
* Returns the whole string left, including possibly more separators.
*/
inline string_type string_remaining() const noexcept
{
return m_string->substr(m_position);
}
inline void append(string_type& str) const noexcept
{
str.append(m_string->substr(m_position, size()));
}
inline void append_remaining(string_type& str) const noexcept
{
str.append(m_string->substr(m_position));
}
}; // class const_iterator
inline const_iterator cbegin(char_type c = ';') const noexcept
{
#if 0
if (G_UNLIKELY(position >= size()))
return false;
return string_at_unchecked(position, str);
#endif
str = nullptr;
return false;
return const_iterator(&m_string, m_separator, 0);
}
inline bool has_subparams_at_unchecked(unsigned int position) const
inline const_iterator cend() const noexcept
{
return false;
return const_iterator(&m_string, m_separator);
}
inline Sequence subparams_at_unchecked(unsigned int position) const
inline const_iterator begin(char_type c = ';') const noexcept
{
return Sequence{};
return cbegin();
}
struct vte_seq** seq_ptr() { return &m_seq; }
inline explicit operator bool() const { return m_seq != nullptr; }
private:
struct vte_seq *m_seq{nullptr};
char const* type_string() const;
char const* command_string() const;
};
inline const_iterator end() const noexcept
{
return cend();
}
typedef Sequence Params;
}; // class StringTokeniser
} // namespace parser
......
/*
* Copyright © 2018 Christian Persch
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License) or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not) see <https://www.gnu.org/licenses/>.
*/
#if !defined(_VTE_OSC)
#error "Must define _VTE_OSC before including this file"
#endif
/* _VTE_OSC(DTTERM_CWD, 3) Conflicts with XTERM_SET_XPROPERTY */
_VTE_OSC(EMACS_51, 51)
_VTE_OSC(ITERM2_GROWL, 9)
_VTE_OSC(ITERM2_133, 133)
_VTE_OSC(ITERM2_1337, 1337)
_VTE_OSC(KONSOLE_30, 30)
_VTE_OSC(KONSOLE_31, 31)
/* _VTE_OSC(MINTTY_CLIPBOARD_COPY_WINDOW_TITLE, 7721) */
/* _VTE_OSC(MINTTY_CHANGE_FONT_SIZE, 7770) */
/* _VTE_OSC(MINTTY_QUERY_FONT_SUPPORTS_CHARACTERS, 7771) */
/* _VTE_OSC(MINTTY_CHANGE_FONT_AND_WINDOW_SIZE, 7777) */
/* _VTE_OSC(MINTTY_INDIC_WIDE, 77119) out of range */
_VTE_OSC(RLOGIN_SET_KANJI_MODE, 800)
_VTE_OSC(RLOGIN_SPEECH, 801)
/* _VTE_OSC(RXVT_MENU, 10) * Conflics with XTERM_SET_COLOR_TEXT_FG */
_VTE_OSC(RXVT_SET_BACKGROUND_PIXMAP, 20)
_VTE_OSC(RXVT_SET_COLOR_FG, 39)
_VTE_OSC(RXVT_SET_COLOR_BG, 49)
_VTE_OSC(RXVT_DUMP_SCREEN, 55)
_VTE_OSC(URXVT_SET_LOCALE, 701)
_VTE_OSC(URXVT_VERSION, 702)
_VTE_OSC(URXVT_SET_COLOR_TEXT_ITALIC, 704)
_VTE_OSC(URXVT_SET_COLOR_TEXT_BOLD, 706)
_VTE_OSC(URXVT_SET_COLOR_UNDERLINE, 707)
_VTE_OSC(URXVT_SET_COLOR_BORDER, 708)
_VTE_OSC(URXVT_SET_FONT, 710)
_VTE_OSC(URXVT_SET_FONT_BOLD, 711)
_VTE_OSC(URXVT_SET_FONT_ITALIC, 712)
_VTE_OSC(URXVT_SET_FONT_BOLD_ITALIC, 713)
_VTE_OSC(URXVT_VIEW_UP, 720)
_VTE_OSC(URXVT_VIEW_DOWN, 721)
_VTE_OSC(URXVT_EXTENSION, 777)
_VTE_OSC(VTECWF, 6)
_VTE_OSC(VTECWD, 7)
_VTE_OSC(VTEHYPER, 8)
_VTE_OSC(XTERM_SET_WINDOW_AND_ICON_TITLE, 0)
_VTE_OSC(XTERM_SET_ICON_TITLE, 1)
_VTE_OSC(XTERM_SET_WINDOW_TITLE, 2)
_VTE_OSC(XTERM_SET_XPROPERTY, 3)
_VTE_OSC(XTERM_SET_COLOR, 4)
_VTE_OSC(XTERM_SET_COLOR_SPECIAL, 5)
/* _VTE_OSC(XTERM_SET_COLOR_MODE, 6) Conflict with our own OSC 6 VTECWF; so use 106 */
_VTE_OSC(XTERM_SET_COLOR_TEXT_FG, 10)
_VTE_OSC(XTERM_SET_COLOR_TEXT_BG, 11)
_VTE_OSC(XTERM_SET_COLOR_CURSOR_BG, 12)
_VTE_OSC(XTERM_SET_COLOR_MOUSE_CURSOR_FG, 13)
_VTE_OSC(XTERM_SET_COLOR_MOUSE_CURSOR_BG, 14)
_VTE_OSC(XTERM_SET_COLOR_TEK_FG, 15)
_VTE_OSC(XTERM_SET_COLOR_TEK_BG, 16)
_VTE_OSC(XTERM_SET_COLOR_HIGHLIGHT_BG, 17)
_VTE_OSC(XTERM_SET_COLOR_TEK_CURSOR, 18)
_VTE_OSC(XTERM_SET_COLOR_HIGHLIGHT_FG, 19)
_VTE_OSC(XTERM_LOGFILE, 46)
_VTE_OSC(XTERM_SET_FONT, 50)
_VTE_OSC(XTERM_SET_XSELECTION, 52)
_VTE_OSC(XTERM_RESET_COLOR, 104)
_VTE_OSC(XTERM_RESET_COLOR_SPECIAL, 105)
_VTE_OSC(XTERM_SET_COLOR_MODE, 106)
_VTE_OSC(XTERM_RESET_COLOR_TEXT_FG, 110)
_VTE_OSC(XTERM_RESET_COLOR_TEXT_BG, 111)
_VTE_OSC(XTERM_RESET_COLOR_CURSOR_BG, 112)
_VTE_OSC(XTERM_RESET_COLOR_MOUSE_CURSOR_FG, 113)
_VTE_OSC(XTERM_RESET_COLOR_MOUSE_CURSOR_BG, 114)
_VTE_OSC(XTERM_RESET_COLOR_TEK_FG, 115)
_VTE_OSC(XTERM_RESET_COLOR_TEK_BG, 116)
_VTE_OSC(XTERM_RESET_COLOR_HIGHLIGHT_BG, 117)
_VTE_OSC(XTERM_RESET_COLOR_TEK_CURSOR, 118)
_VTE_OSC(XTERM_RESET_COLOR_HIGHLIGHT_FG, 119)
_VTE_OSC(YF_RQGWR, 8900)
/*
* Copyright © 2018 Christian Persch
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <assert.h>
#include <cstdint>
#include <cstring>
/*
* vte_seq_string_t:
*
* A type to hold the argument string of a DSC or OSC sequence.
*/
typedef struct vte_seq_string_t {
uint32_t capacity;
uint32_t len;
uint32_t* buf;
} vte_seq_string_t;
#define VTE_SEQ_STRING_DEFAULT_CAPACITY (1 << 7) /* must be power of two */
#define VTE_SEQ_STRING_MAX_CAPACITY (1 << 12)
/*
* vte_seq_string_init:
*
* Returns: a new #vte_seq_string_t
*/
static inline void vte_seq_string_init(vte_seq_string_t* str) noexcept
{
str->capacity = VTE_SEQ_STRING_DEFAULT_CAPACITY;
str->len = 0;
str->buf = (uint32_t*)g_malloc0_n(str->capacity, sizeof(uint32_t));
}
/*
* vte_seq_string_free:
* @string:
*
* Frees @string's storage and itself.
*/
static inline void vte_seq_string_free(vte_seq_string_t* str) noexcept
{
g_free(str->buf);
}
/*
* vte_seq_string_ensure_capacity:
* @string:
*
* If @string's length is at capacity, and capacity is not maximal,
* expands the string's capacity.
*
* Returns: %true if the string has capacity for at least one more character
*/
static inline bool vte_seq_string_ensure_capacity(vte_seq_string_t* str) noexcept
{
if (str->len < str->capacity)
return true;
if (str->capacity >= VTE_SEQ_STRING_MAX_CAPACITY)
return false;
str->capacity *= 2;
str->buf = (uint32_t*)g_realloc_n(str->buf, str->capacity, sizeof(uint32_t));
return true;
}
/*
* vte_seq_string_push:
* @string:
* @c: a character
*
* Appends @c to @str, or iff @str already has maximum length, does nothing.
*
* Returns: %true if the character was appended
*/
static inline bool vte_seq_string_push(vte_seq_string_t* str,
uint32_t c) noexcept
{
if (!vte_seq_string_ensure_capacity(str))
return false;
str->buf[str->len++] = c;
return true;
}
/*
* vte_seq_string_finish:
* @string:
*
* Finishes @string; after this no more vte_seq_string_push() calls
* are allowed until the string is reset with vte_seq_string_reset().
*/
static inline void vte_seq_string_finish(vte_seq_string_t* str)
{
}
/*
* vte_seq_string_reset:
* @string:
*
* Resets @string.
*/
static inline void vte_seq_string_reset(vte_seq_string_t* str) noexcept
{
/* Zero length. However, don't clear the buffer, nor shrink the capacity. */
str->len = 0;
}
/*
* vte_seq_string_get:
* @string:
* @len: location to store the buffer length in code units
*
* Returns: the string's buffer as an array of uint32_t code units
*/
static constexpr inline uint32_t* vte_seq_string_get(vte_seq_string_t const* str,
size_t* len) noexcept
{
assert(len != nullptr);
*len = str->len;
return str->buf;
}
......@@ -25,6 +25,8 @@
#include <string>
#include <vector>
using namespace std::literals;
#include <glib.h>
#include "parser.hh"
......@@ -155,6 +157,15 @@ public:
set_final(f);
}
vte_seq_builder(unsigned int type,
std::u32string const& str)
: m_arg_str(str)
{
memset(&m_seq, 0, sizeof(m_seq));
m_seq.type = type;
set_final(0);
}
~vte_seq_builder() = default;
void set_final(uint32_t raw) { m_seq.terminator = raw; }
......@@ -189,8 +200,23 @@ public:
}
}
void set_string(std::u32string const& str)
{
m_arg_str = str;
}
enum VariantST {
ST_NONE,
ST_DEFAULT,
ST_C0,
ST_C1,
ST_BEL
};