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