1 /*
2  * Copyright (C) 2002 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #define GLIB_DISABLE_DEPRECATION_WARNINGS /* FIXME */
20 
21 #include <vice.h>
22 
23 #include <sys/types.h>
24 #include <string.h>
25 #include <glib-object.h>
26 #include "debug.h"
27 #include "caps.hh"
28 #include "matcher.hh"
29 #include "table.hh"
30 
31 struct _vte_matcher {
32     _vte_matcher_match_func match; /* shortcut to the most common op */
33     struct _vte_matcher_impl *impl;
34     GValueArray *free_params;
35 };
36 
37 static GMutex _vte_matcher_mutex;
38 static struct _vte_matcher *_vte_matcher_singleton = NULL;
39 static int _vte_matcher_ref_count = 0;
40 
41 static struct _vte_matcher_impl dummy_vte_matcher_table = {
42     &_vte_matcher_table
43 };
44 
45 /* Add a string to the matcher. */
_vte_matcher_add(const struct _vte_matcher * matcher,const char * pattern,gssize length,sequence_handler_t handler)46 static void _vte_matcher_add(const struct _vte_matcher *matcher,
47                              const char *pattern,
48                              gssize length,
49                              sequence_handler_t handler)
50 {
51     matcher->impl->klass->add(matcher->impl, pattern, length, handler);
52 }
53 
54 /* Loads all sequences into matcher */
_vte_matcher_init(struct _vte_matcher * matcher)55 static void _vte_matcher_init(struct _vte_matcher *matcher)
56 {
57     _vte_debug_print(VTE_DEBUG_LIFECYCLE, "_vte_matcher_init()\n");
58 
59     unsigned int n_entries;
60     auto entries = _vte_get_matcher_entries(&n_entries);
61 
62     for (unsigned int e = 0; e < n_entries; e++) {
63         char const* code = entries[e].seq;
64 
65         /* Escape sequences from \e@ to \e_ have a C1 counterpart
66             * with the eighth bit set instead of a preceding '\x1b'.
67             * This is encoded in the current encoding, e.g. in UTF-8
68             * the C1 CSI (U+009B) becomes \xC2\x9B.
69             *
70             * When matching, the bytestream is already decoded to
71             * Unicode codepoints.  In the "code" string, each byte
72             * is interpreted as a Unicode codepoint (in other words,
73             * Latin-1 is assumed).  So '\x80' .. '\x9F' bytes
74             * (e.g. the byte '\x9B' for CSI) are the right choice here.
75             *
76             * For each sequence containing N occurrences of \e@ to \e_,
77             * we create 2^N variants, by replacing every subset of them
78             * with their C1 counterpart.
79             */
80         int variants = 1;
81         for (int i = 0; code[i] != '\0'; i++) {
82             if (code[i] == '\x1B' && code[i + 1] >= '@' && code[i + 1] <= '_') {
83                 variants <<= 1;
84             }
85         }
86         for (int n = 0; n < variants; n++) {
87             char* c1 = g_strdup(code);
88             int k = 0;
89             for (int i = 0; c1[i] != '\0'; i++) {
90                 if (c1[i] == '\x1B' && c1[i + 1] >= '@' && c1[i + 1] <= '_') {
91                     if (n & (1 << k)) {
92                         memmove(c1 + i, c1 + i + 1, strlen(c1 + i + 1) + 1);
93                         c1[i] += 0x40;
94                     }
95                     k++;
96                 }
97             }
98             _vte_matcher_add(matcher, c1, strlen(c1), entries[e].handler);
99             g_free(c1);
100         }
101     }
102 
103     _VTE_DEBUG_IF(VTE_DEBUG_MATCHER) {
104         g_printerr("Matcher contents:\n");
105         _vte_matcher_print(matcher);
106         g_printerr("\n");
107     }
108 }
109 
110 /* Allocates new matcher structure. */
_vte_matcher_create(void)111 static struct _vte_matcher *_vte_matcher_create(void)
112 {
113     struct _vte_matcher *ret = NULL;
114 
115     _vte_debug_print(VTE_DEBUG_LIFECYCLE, "_vte_matcher_create()\n");
116     ret = g_slice_new(struct _vte_matcher);
117     ret->impl = &dummy_vte_matcher_table;
118     ret->match = NULL;
119     ret->free_params = NULL;
120 
121     return ret;
122 }
123 
124 /* Noone uses this matcher, free it. */
_vte_matcher_destroy(struct _vte_matcher * matcher)125 static void _vte_matcher_destroy(struct _vte_matcher *matcher)
126 {
127     _vte_debug_print(VTE_DEBUG_LIFECYCLE, "_vte_matcher_destroy()\n");
128     if (matcher->free_params != NULL) {
129         g_value_array_free (matcher->free_params);
130     }
131     if (matcher->match != NULL) {
132         /* do not call destroy on dummy values */
133         matcher->impl->klass->destroy(matcher->impl);
134     }
135     g_slice_free(struct _vte_matcher, matcher);
136 }
137 
138 /* Create and init matcher. */
_vte_matcher_new(void)139 struct _vte_matcher *_vte_matcher_new(void)
140 {
141     struct _vte_matcher *ret = NULL;
142 
143     g_mutex_lock(&_vte_matcher_mutex);
144 
145     if (_vte_matcher_ref_count++ == 0) {
146         g_assert(_vte_matcher_singleton == NULL);
147         ret = _vte_matcher_create();
148 
149         if (ret->match == NULL) {
150             ret->impl = ret->impl->klass->create();
151             ret->match = ret->impl->klass->match;
152             _vte_matcher_init(ret);
153         }
154         _vte_matcher_singleton = ret;
155     }
156 
157     g_mutex_unlock(&_vte_matcher_mutex);
158     return _vte_matcher_singleton;
159 }
160 
161 /* Free a matcher. */
_vte_matcher_free(struct _vte_matcher * matcher)162 void _vte_matcher_free(struct _vte_matcher *matcher)
163 {
164     g_assert(_vte_matcher_singleton != NULL);
165     g_mutex_lock(&_vte_matcher_mutex);
166     if (--_vte_matcher_ref_count == 0) {
167         _vte_matcher_destroy(matcher);
168         _vte_matcher_singleton = NULL;
169     }
170     g_mutex_unlock(&_vte_matcher_mutex);
171 }
172 
173 /* Check if a string matches a sequence the matcher knows about. */
_vte_matcher_match(struct _vte_matcher * matcher,const gunichar * pattern,gssize length,sequence_handler_t * handler,const gunichar ** consumed,GValueArray ** array)174 vte_matcher_result_t _vte_matcher_match(struct _vte_matcher *matcher,
175                                         const gunichar *pattern,
176                                         gssize length,
177                                         sequence_handler_t *handler,
178                                         const gunichar **consumed,
179                                         GValueArray **array)
180 {
181     if (G_UNLIKELY (array != NULL && matcher->free_params != NULL)) {
182         *array = matcher->free_params;
183         matcher->free_params = NULL;
184     }
185     return matcher->match(matcher->impl, pattern, length, handler, consumed, array);
186 }
187 
188 /* Dump out the contents of a matcher, mainly for debugging. */
_vte_matcher_print(struct _vte_matcher * matcher)189 void _vte_matcher_print(struct _vte_matcher *matcher)
190 {
191     matcher->impl->klass->print(matcher->impl);
192 }
193 
194 /* Free a parameter array.  Most of the GValue elements can clean up after
195  * themselves, but we're using gpointers to hold unicode character strings, and
196  * we need to free those ourselves. */
_vte_matcher_free_params_array(struct _vte_matcher * matcher,GValueArray * params)197 void _vte_matcher_free_params_array(struct _vte_matcher *matcher, GValueArray *params)
198 {
199     guint i;
200     for (i = 0; i < params->n_values; i++) {
201         auto value = g_value_array_get_nth(params, i);
202         if (G_UNLIKELY (G_VALUE_HOLDS_POINTER(value))) {
203             g_free(g_value_get_pointer(value));
204         } else if (G_UNLIKELY (G_VALUE_HOLDS_BOXED(value))) {
205             g_value_array_free((GValueArray*)g_value_get_boxed(value));
206         }
207     }
208     if (G_UNLIKELY (matcher == NULL || matcher->free_params != NULL)) {
209         g_value_array_free (params);
210     } else {
211         matcher->free_params = params;
212         params->n_values = 0;
213     }
214 }
215