1 /*
2 * Copyright (C) 2002 Red Hat, Inc.
3 *
4 * This is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Library General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <config.h>
20 #include <sys/types.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <glib-object.h>
28 #include "debug.h"
29 #include "iso2022.h"
30 #include "table.h"
31
32 /* Table info. */
33 #define VTE_TABLE_MAX_LITERAL (128 + 32)
34 #define _vte_table_map_literal(__c) \
35 (((__c) < (VTE_TABLE_MAX_LITERAL)) ? (__c) : 0)
36 #define _vte_table_is_numeric(__c) \
37 (((__c) >= '0') && ((__c) <= '9'))
38 #define _vte_table_is_numeric_list(__c) \
39 ((((__c) >= '0') && ((__c) <= '9')) || (__c) == ';')
40
41 struct _vte_table {
42 struct _vte_matcher_impl impl;
43 GQuark resultq;
44 const char *result;
45 unsigned char *original;
46 gssize original_length;
47 int increment;
48 struct _vte_table *table_string;
49 struct _vte_table *table_number;
50 struct _vte_table *table_number_list;
51 struct _vte_table **table;
52 };
53
54 /* Argument info. */
55 enum _vte_table_argtype {
56 _vte_table_arg_number=0,
57 _vte_table_arg_string,
58 _vte_table_arg_char
59 };
60 struct _vte_table_arginfo {
61 const gunichar *start;
62 struct _vte_table_arginfo *next;
63 guint type:2;
64 guint length:30;
65 };
66 #define MAX_STACK 16
67 struct _vte_table_arginfo_head {
68 guint stack_allocated;
69 struct _vte_table_arginfo *list;
70 struct _vte_table_arginfo stack[MAX_STACK];
71 };
72
73 static void
_vte_table_arginfo_head_init(struct _vte_table_arginfo_head * head)74 _vte_table_arginfo_head_init(struct _vte_table_arginfo_head *head)
75 {
76 head->list = NULL;
77 head->stack_allocated = 0;
78 }
79 static inline struct _vte_table_arginfo*
_vte_table_arginfo_alloc(struct _vte_table_arginfo_head * head)80 _vte_table_arginfo_alloc(struct _vte_table_arginfo_head *head)
81 {
82 struct _vte_table_arginfo *info;
83 if (G_LIKELY (head->stack_allocated < G_N_ELEMENTS(head->stack))) {
84 info = &head->stack[head->stack_allocated++];
85 } else {
86 info = g_slice_new (struct _vte_table_arginfo);
87 }
88 info->next = head->list;
89 head->list = info;
90 return info;
91 }
92 static void
_vte_table_arginfo_head_revert(struct _vte_table_arginfo_head * head,struct _vte_table_arginfo * last)93 _vte_table_arginfo_head_revert(struct _vte_table_arginfo_head *head, struct _vte_table_arginfo *last)
94 {
95 struct _vte_table_arginfo *info;
96 info = head->list;
97 head->list = last->next;
98 if (last >= &head->stack[0] &&
99 last < &head->stack[G_N_ELEMENTS(head->stack)]){
100 head->stack_allocated = last - &head->stack[0];
101 }
102 do {
103 struct _vte_table_arginfo *next = info->next;
104 if (info >= &head->stack[0] &&
105 info < &head->stack[G_N_ELEMENTS(head->stack)]){
106 break;
107 }
108 g_slice_free(struct _vte_table_arginfo, info);
109 if (info == last) {
110 break;
111 }
112 info = next;
113 }while (TRUE);
114 }
115 static struct _vte_table_arginfo *
_vte_table_arginfo_head_reverse(struct _vte_table_arginfo_head * head)116 _vte_table_arginfo_head_reverse(struct _vte_table_arginfo_head *head)
117 {
118 struct _vte_table_arginfo *prev = NULL;
119 while (head->list) {
120 struct _vte_table_arginfo *next = head->list->next;
121
122 head->list->next = prev;
123
124 prev = head->list;
125 head->list = next;
126 }
127 return prev;
128 }
129 static void
_vte_table_arginfo_head_finalize(struct _vte_table_arginfo_head * head)130 _vte_table_arginfo_head_finalize(struct _vte_table_arginfo_head *head)
131 {
132 struct _vte_table_arginfo *info, *next;
133 for (info = head->list; info != NULL; info = next) {
134 next = info->next;
135 if (info >= &head->stack[0] &&
136 info < &head->stack[G_N_ELEMENTS(head->stack)]){
137 continue;
138 }
139 g_slice_free(struct _vte_table_arginfo, info);
140 }
141 }
142
143 /* Create an empty, one-level table. */
144 struct _vte_table *
_vte_table_new(void)145 _vte_table_new(void)
146 {
147 struct _vte_table * ret;
148 ret = g_slice_new0(struct _vte_table);
149 ret->impl.klass = &_vte_matcher_table;
150 return ret;
151 }
152
153 static struct _vte_table **
_vte_table_literal_new(void)154 _vte_table_literal_new(void)
155 {
156 return g_new0(struct _vte_table *, VTE_TABLE_MAX_LITERAL);
157 }
158
159 /* Free a table. */
160 void
_vte_table_free(struct _vte_table * table)161 _vte_table_free(struct _vte_table *table)
162 {
163 unsigned int i;
164 if (table->table != NULL) {
165 for (i = 0; i < VTE_TABLE_MAX_LITERAL; i++) {
166 if (table->table[i] != NULL) {
167 _vte_table_free(table->table[i]);
168 }
169 }
170 g_free(table->table);
171 }
172 if (table->table_string != NULL) {
173 _vte_table_free(table->table_string);
174 }
175 if (table->table_number != NULL) {
176 _vte_table_free(table->table_number);
177 }
178 if (table->table_number_list != NULL) {
179 _vte_table_free(table->table_number_list);
180 }
181 if (table->original_length == 0) {
182 g_assert(table->original == NULL);
183 } else {
184 g_assert(table->original != NULL);
185 }
186 if (table->original != NULL) {
187 g_free(table->original);
188 }
189 g_slice_free(struct _vte_table, table);
190 }
191
192 /* Add a string to the tree with the given increment value. */
193 static void
_vte_table_addi(struct _vte_table * table,const unsigned char * original,gssize original_length,const char * pattern,gssize length,const char * result,GQuark quark,int inc)194 _vte_table_addi(struct _vte_table *table,
195 const unsigned char *original, gssize original_length,
196 const char *pattern, gssize length,
197 const char *result, GQuark quark, int inc)
198 {
199 int i;
200 guint8 check;
201 struct _vte_table *subtable;
202
203 if (original_length == -1) {
204 original_length = strlen((char *) original);
205 }
206 if (length == -1) {
207 length = strlen(pattern);
208 }
209
210 /* If this is the terminal node, set the result. */
211 if (length == 0) {
212 if (table->result != NULL)
213 _vte_debug_print (VTE_DEBUG_PARSE,
214 "`%s' and `%s' are indistinguishable.\n",
215 table->result, result);
216
217 table->resultq = g_quark_from_string(result);
218 table->result = g_quark_to_string(table->resultq);
219 if (table->original != NULL) {
220 g_free(table->original);
221 }
222 table->original = g_memdup(original, original_length);
223 table->original_length = original_length;
224 table->increment = inc;
225 return;
226 }
227
228 /* All of the interesting arguments begin with '%'. */
229 if (pattern[0] == '%') {
230 /* Handle an increment. */
231 if (pattern[1] == 'i') {
232 _vte_table_addi(table, original, original_length,
233 pattern + 2, length - 2,
234 result, quark, inc + 1);
235 return;
236 }
237
238 /* Handle numeric parameters. */
239 if ((pattern[1] == 'd') ||
240 (pattern[1] == '2') ||
241 (pattern[1] == '3')) {
242 /* Create a new subtable. */
243 if (table->table_number == NULL) {
244 subtable = _vte_table_new();
245 table->table_number = subtable;
246 } else {
247 subtable = table->table_number;
248 }
249 /* Add the rest of the string to the subtable. */
250 _vte_table_addi(subtable, original, original_length,
251 pattern + 2, length - 2,
252 result, quark, inc);
253 return;
254 }
255
256 /* Handle variable-length parameters. */
257 if ((pattern[1] == 'm') ||
258 (pattern[1] == 'M')) {
259 /* Build the "new" original using the initial portion
260 * of the original string and what's left after this
261 * specifier. */
262 if (pattern[1] == 'm') {
263 int initial;
264 GByteArray *b;
265
266 initial = original_length - length;
267 /* 0 args; we use 'M' to signal that zero is
268 * not allowed. */
269 b = g_byte_array_new();
270 g_byte_array_set_size(b, 0);
271 g_byte_array_append(b, original, initial);
272 g_byte_array_append(b, (const guint8*)pattern + 2, length - 2);
273 _vte_table_addi(table, b->data, b->len,
274 (const char *)b->data + initial,
275 b->len - initial,
276 result, quark, inc);
277 g_byte_array_free(b, TRUE);
278 }
279 /* Create a new subtable. */
280 if (table->table_number_list == NULL) {
281 subtable = _vte_table_new();
282 table->table_number_list = subtable;
283 } else {
284 subtable = table->table_number_list;
285 }
286 /* Add the rest of the string to the subtable. */
287 _vte_table_addi(subtable, original, original_length,
288 pattern + 2, length - 2,
289 result, quark, inc);
290 return;
291 }
292
293 /* Handle string parameters. */
294 if (pattern[1] == 's') {
295 /* It must have a terminator. */
296 g_assert(length >= 3);
297 /* Create a new subtable. */
298 if (table->table_string == NULL) {
299 subtable = _vte_table_new();
300 table->table_string = subtable;
301 } else {
302 subtable = table->table_string;
303 }
304 /* Add the rest of the string to the subtable. */
305 _vte_table_addi(subtable, original, original_length,
306 pattern + 2, length - 2,
307 result, quark, inc);
308 return;
309 }
310
311 /* Handle an escaped '%'. */
312 if (pattern[1] == '%') {
313 /* Create a new subtable. */
314 if (table->table == NULL) {
315 table->table = _vte_table_literal_new();
316 subtable = _vte_table_new();
317 table->table['%'] = subtable;
318 } else
319 if (table->table['%'] == NULL) {
320 subtable = _vte_table_new();
321 table->table['%'] = subtable;
322 } else {
323 subtable = table->table['%'];
324 }
325 /* Add the rest of the string to the subtable. */
326 _vte_table_addi(subtable, original, original_length,
327 pattern + 2, length - 2,
328 result, quark, inc);
329 return;
330 }
331
332 /* Handle a parameter character. */
333 if (pattern[1] == '+') {
334 /* It must have an addend. */
335 g_assert(length >= 3);
336 /* Fill in all of the table entries above the given
337 * character value. */
338 for (i = pattern[2]; i < VTE_TABLE_MAX_LITERAL; i++) {
339 /* Create a new subtable. */
340 if (table->table == NULL) {
341 table->table = _vte_table_literal_new();
342 subtable = _vte_table_new();
343 table->table[i] = subtable;
344 } else
345 if (table->table[i] == NULL) {
346 subtable = _vte_table_new();
347 table->table[i] = subtable;
348 } else {
349 subtable = table->table[i];
350 }
351 /* Add the rest of the string to the subtable. */
352 _vte_table_addi(subtable,
353 original, original_length,
354 pattern + 3, length - 3,
355 result, quark, inc);
356 }
357 /* Also add a subtable for higher characters. */
358 if (table->table == NULL) {
359 table->table = _vte_table_literal_new();
360 subtable = _vte_table_new();
361 table->table[0] = subtable;
362 } else
363 if (table->table[0] == NULL) {
364 subtable = _vte_table_new();
365 table->table[0] = subtable;
366 } else {
367 subtable = table->table[0];
368 }
369 /* Add the rest of the string to the subtable. */
370 _vte_table_addi(subtable, original, original_length,
371 pattern + 3, length - 3,
372 result, quark, inc);
373 return;
374 }
375 }
376
377 /* A literal (or an unescaped '%', which is also a literal). */
378 check = (guint8) pattern[0];
379 g_assert(check < VTE_TABLE_MAX_LITERAL);
380 if (table->table == NULL) {
381 table->table = _vte_table_literal_new();
382 subtable = _vte_table_new();
383 table->table[check] = subtable;
384 } else
385 if (table->table[check] == NULL) {
386 subtable = _vte_table_new();
387 table->table[check] = subtable;
388 } else {
389 subtable = table->table[check];
390 }
391
392 /* Add the rest of the string to the subtable. */
393 _vte_table_addi(subtable, original, original_length,
394 pattern + 1, length - 1,
395 result, quark, inc);
396 }
397
398 /* Add a string to the matching tree. */
399 void
_vte_table_add(struct _vte_table * table,const char * pattern,gssize length,const char * result,GQuark quark)400 _vte_table_add(struct _vte_table *table,
401 const char *pattern, gssize length,
402 const char *result, GQuark quark)
403 {
404 _vte_table_addi(table,
405 (const unsigned char *) pattern, length,
406 pattern, length,
407 result, quark, 0);
408 }
409
410 /* Match a string in a subtree. */
411 static const char *
_vte_table_matchi(struct _vte_table * table,const gunichar * candidate,gssize length,const char ** res,const gunichar ** consumed,GQuark * quark,unsigned char ** original,gssize * original_length,struct _vte_table_arginfo_head * params)412 _vte_table_matchi(struct _vte_table *table,
413 const gunichar *candidate, gssize length,
414 const char **res, const gunichar **consumed, GQuark *quark,
415 unsigned char **original, gssize *original_length,
416 struct _vte_table_arginfo_head *params)
417 {
418 int i = 0;
419 struct _vte_table *subtable = NULL;
420 struct _vte_table_arginfo *arginfo;
421
422 /* Check if this is a result node. */
423 if (table->result != NULL) {
424 *consumed = candidate;
425 *original = table->original;
426 *original_length = table->original_length;
427 *res = table->result;
428 *quark = table->resultq;
429 return table->result;
430 }
431
432 /* If we're out of data, but we still have children, return the empty
433 * string. */
434 if (G_UNLIKELY (length == 0)) {
435 *consumed = candidate;
436 return "";
437 }
438
439 /* Check if this node has a string disposition. */
440 if (table->table_string != NULL) {
441 /* Iterate over all non-terminator values. */
442 subtable = table->table_string;
443 for (i = 0; i < length; i++) {
444 if ((subtable->table != NULL) &&
445 (subtable->table[_vte_table_map_literal(candidate[i])] != NULL)) {
446 break;
447 }
448 }
449 /* Save the parameter info. */
450 arginfo = _vte_table_arginfo_alloc(params);
451 arginfo->type = _vte_table_arg_string;
452 arginfo->start = candidate;
453 arginfo->length = i;
454 /* Continue. */
455 return _vte_table_matchi(subtable, candidate + i, length - i,
456 res, consumed, quark,
457 original, original_length, params);
458 }
459
460 /* Check if this could be a list. */
461 if ((_vte_table_is_numeric_list(candidate[0])) &&
462 (table->table_number_list != NULL)) {
463 const char *local_result;
464
465 subtable = table->table_number_list;
466 /* Iterate over all numeric characters and ';'. */
467 for (i = 1; i < length; i++) {
468 if (!_vte_table_is_numeric_list(candidate[i])) {
469 break;
470 }
471 }
472 /* Save the parameter info. */
473 arginfo = _vte_table_arginfo_alloc(params);
474 arginfo->type = _vte_table_arg_number;
475 arginfo->start = candidate;
476 arginfo->length = i;
477
478 /* Try and continue. */
479 local_result = _vte_table_matchi(subtable,
480 candidate + i, length - i,
481 res, consumed, quark,
482 original, original_length,
483 params);
484 if (local_result != NULL) {
485 return local_result;
486 }
487 _vte_table_arginfo_head_revert (params, arginfo);
488
489 /* try again */
490 }
491
492 /* Check if this could be a number. */
493 if ((_vte_table_is_numeric(candidate[0])) &&
494 (table->table_number != NULL)) {
495 subtable = table->table_number;
496 /* Iterate over all numeric characters. */
497 for (i = 1; i < length; i++) {
498 if (!_vte_table_is_numeric(candidate[i])) {
499 break;
500 }
501 }
502 /* Save the parameter info. */
503 arginfo = _vte_table_arginfo_alloc(params);
504 arginfo->type = _vte_table_arg_number;
505 arginfo->start = candidate;
506 arginfo->length = i;
507 /* Continue. */
508 return _vte_table_matchi(subtable, candidate + i, length - i,
509 res, consumed, quark,
510 original, original_length, params);
511 }
512
513 /* Check for an exact match. */
514 if ((table->table != NULL) &&
515 (table->table[_vte_table_map_literal(candidate[0])] != NULL)) {
516 subtable = table->table[_vte_table_map_literal(candidate[0])];
517 /* Save the parameter info. */
518 arginfo = _vte_table_arginfo_alloc(params);
519 arginfo->type = _vte_table_arg_char;
520 arginfo->start = candidate;
521 arginfo->length = 1;
522 /* Continue. */
523 return _vte_table_matchi(subtable, candidate + 1, length - 1,
524 res, consumed, quark,
525 original, original_length, params);
526 }
527
528 /* If there's nothing else to do, then we can't go on. Keep track of
529 * where we are. */
530 *consumed = candidate;
531 return NULL;
532 }
533
534 static void
_vte_table_extract_numbers(GValueArray ** array,struct _vte_table_arginfo * arginfo,long increment)535 _vte_table_extract_numbers(GValueArray **array,
536 struct _vte_table_arginfo *arginfo, long increment)
537 {
538 GValue value = {0,};
539 gssize i;
540
541 g_value_init(&value, G_TYPE_LONG);
542 i = 0;
543 do {
544 long total = 0;
545 for (; i < arginfo->length && arginfo->start[i] != ';'; i++) {
546 gint v = g_unichar_digit_value (arginfo->start[i]);
547 total *= 10;
548 total += v == -1 ? 0 : v;
549 }
550 if (G_UNLIKELY (*array == NULL)) {
551 *array = g_value_array_new(1);
552 }
553 g_value_set_long(&value, total);
554 g_value_array_append(*array, &value);
555 } while (i++ < arginfo->length);
556 g_value_unset(&value);
557 }
558
559 static void
_vte_table_extract_string(GValueArray ** array,struct _vte_table_arginfo * arginfo)560 _vte_table_extract_string(GValueArray **array,
561 struct _vte_table_arginfo *arginfo)
562 {
563 GValue value = {0,};
564 gunichar *ptr;
565 guint i;
566
567 ptr = g_new(gunichar, arginfo->length + 1);
568 for (i = 0; i < arginfo->length; i++) {
569 ptr[i] = arginfo->start[i] & ~VTE_ISO2022_ENCODED_WIDTH_MASK;
570 }
571 ptr[i] = '\0';
572 g_value_init(&value, G_TYPE_POINTER);
573 g_value_set_pointer(&value, ptr);
574
575 if (G_UNLIKELY (*array == NULL)) {
576 *array = g_value_array_new(1);
577 }
578 g_value_array_append(*array, &value);
579 g_value_unset(&value);
580 }
581
582 static void
_vte_table_extract_char(GValueArray ** array,struct _vte_table_arginfo * arginfo,long increment)583 _vte_table_extract_char(GValueArray **array,
584 struct _vte_table_arginfo *arginfo, long increment)
585 {
586 GValue value = {0,};
587
588 g_value_init(&value, G_TYPE_LONG);
589 g_value_set_long(&value, *(arginfo->start) - increment);
590
591 if (G_UNLIKELY (*array == NULL)) {
592 *array = g_value_array_new(1);
593 }
594 g_value_array_append(*array, &value);
595 g_value_unset(&value);
596 }
597
598 /* Check if a string matches something in the tree. */
599 const char *
_vte_table_match(struct _vte_table * table,const gunichar * candidate,gssize length,const char ** res,const gunichar ** consumed,GQuark * quark,GValueArray ** array)600 _vte_table_match(struct _vte_table *table,
601 const gunichar *candidate, gssize length,
602 const char **res, const gunichar **consumed,
603 GQuark *quark, GValueArray **array)
604 {
605 struct _vte_table *head;
606 const gunichar *dummy_consumed;
607 const char *dummy_res;
608 GQuark dummy_quark;
609 GValueArray *dummy_array;
610 const char *ret;
611 unsigned char *original, *p;
612 gssize original_length;
613 long increment = 0;
614 int i;
615 struct _vte_table_arginfo_head params;
616 struct _vte_table_arginfo *arginfo;
617
618 /* Clean up extracted parameters. */
619 if (G_UNLIKELY (res == NULL)) {
620 res = &dummy_res;
621 }
622 *res = NULL;
623 if (G_UNLIKELY (consumed == NULL)) {
624 consumed = &dummy_consumed;
625 }
626 *consumed = candidate;
627 if (G_UNLIKELY (quark == NULL)) {
628 quark = &dummy_quark;
629 }
630 *quark = 0;
631 if (G_UNLIKELY (array == NULL)) {
632 dummy_array = NULL;
633 array = &dummy_array;
634 }
635
636 /* Provide a fast path for the usual "not a sequence" cases. */
637 if (G_LIKELY (length == 0 || candidate == NULL)) {
638 return NULL;
639 }
640
641 /* If there's no literal path, and no generic path, and the numeric
642 * path isn't available, then it's not a sequence, either. */
643 if (table->table == NULL ||
644 table->table[_vte_table_map_literal(candidate[0])] == NULL) {
645 if (table->table_string == NULL) {
646 if (table->table_number == NULL ||
647 !_vte_table_is_numeric(candidate[0])){
648 if (table->table_number_list == NULL ||
649 !_vte_table_is_numeric_list(candidate[0])){
650 /* No match. */
651 return NULL;
652 }
653 }
654 }
655 }
656
657 /* Check for a literal match. */
658 for (i = 0, head = table; i < length && head != NULL; i++) {
659 if (head->table == NULL) {
660 head = NULL;
661 } else {
662 head = head->table[_vte_table_map_literal(candidate[i])];
663 }
664 }
665 if (head != NULL && head->result != NULL) {
666 /* Got a literal match. */
667 *consumed = candidate + i;
668 *res = head->result;
669 *quark = head->resultq;
670 return *res;
671 }
672
673 _vte_table_arginfo_head_init (¶ms);
674
675 /* Check for a pattern match. */
676 ret = _vte_table_matchi(table, candidate, length,
677 res, consumed, quark,
678 &original, &original_length,
679 ¶ms);
680 *res = ret;
681
682 /* If we got a match, extract the parameters. */
683 if (ret != NULL && ret[0] != '\0' && array != &dummy_array) {
684 g_assert(original != NULL);
685 p = original;
686 arginfo = _vte_table_arginfo_head_reverse (¶ms);
687 do {
688 /* All of the interesting arguments begin with '%'. */
689 if (p[0] == '%') {
690 /* Handle an increment. */
691 if (p[1] == 'i') {
692 increment++;
693 p += 2;
694 continue;
695 }
696 /* Handle an escaped '%'. */
697 else if (p[1] == '%') {
698 p++;
699 }
700 /* Handle numeric parameters. */
701 else if ((p[1] == 'd') ||
702 (p[1] == '2') ||
703 (p[1] == '3') ||
704 (p[1] == 'm') ||
705 (p[1] == 'M')) {
706 _vte_table_extract_numbers(array,
707 arginfo,
708 increment);
709 p++;
710 }
711 /* Handle string parameters. */
712 else if (p[1] == 's') {
713 _vte_table_extract_string(array,
714 arginfo);
715 p++;
716 }
717 /* Handle a parameter character. */
718 else if (p[1] == '+') {
719 _vte_table_extract_char(array,
720 arginfo,
721 p[2]);
722 p += 2;
723 } else {
724 _vte_debug_print (VTE_DEBUG_PARSE,
725 "Invalid termcap sequence %s\n",
726 original);
727 }
728 } /* else Literal. */
729 arginfo = arginfo->next;
730 } while (++p < original + original_length && arginfo);
731 }
732
733 /* Clean up extracted parameters. */
734 _vte_table_arginfo_head_finalize (¶ms);
735
736 return ret;
737 }
738
739 static void
_vte_table_printi(struct _vte_table * table,const char * lead,int * count)740 _vte_table_printi(struct _vte_table *table, const char *lead, int *count)
741 {
742 unsigned int i;
743 char *newlead = NULL;
744
745 (*count)++;
746
747 /* Result? */
748 if (table->result != NULL) {
749 g_printerr("%s = `%s'(%d)\n", lead,
750 table->result, table->increment);
751 }
752
753 /* Literal? */
754 for (i = 1; i < VTE_TABLE_MAX_LITERAL; i++) {
755 if ((table->table != NULL) && (table->table[i] != NULL)) {
756 if (i < 32) {
757 newlead = g_strdup_printf("%s^%c", lead,
758 i + 64);
759 } else {
760 newlead = g_strdup_printf("%s%c", lead, i);
761 }
762 _vte_table_printi(table->table[i], newlead, count);
763 g_free(newlead);
764 }
765 }
766
767 /* String? */
768 if (table->table_string != NULL) {
769 newlead = g_strdup_printf("%s{string}", lead);
770 _vte_table_printi(table->table_string,
771 newlead, count);
772 g_free(newlead);
773 }
774
775 /* Number(+)? */
776 if (table->table_number != NULL) {
777 newlead = g_strdup_printf("%s{number}", lead);
778 _vte_table_printi(table->table_number,
779 newlead, count);
780 g_free(newlead);
781 }
782 }
783
784 /* Dump out the contents of a tree. */
785 void
_vte_table_print(struct _vte_table * table)786 _vte_table_print(struct _vte_table *table)
787 {
788 int count = 0;
789 _vte_table_printi(table, "", &count);
790 g_printerr("%d nodes = %ld bytes.\n",
791 count, (long) count * sizeof(struct _vte_table));
792 }
793
794 #ifdef TABLE_MAIN
795 /* Return an escaped version of a string suitable for printing. */
796 static char *
escape(const char * p)797 escape(const char *p)
798 {
799 char *tmp;
800 GString *ret;
801 int i;
802 guint8 check;
803 ret = g_string_new(NULL);
804 for (i = 0; p[i] != '\0'; i++) {
805 tmp = NULL;
806 check = p[i];
807 if (check < 32) {
808 tmp = g_strdup_printf("^%c", check + 64);
809 } else
810 if (check >= 0x80) {
811 tmp = g_strdup_printf("{0x%x}", check);
812 } else {
813 tmp = g_strdup_printf("%c", check);
814 }
815 g_string_append(ret, tmp);
816 g_free(tmp);
817 }
818 return g_string_free(ret, FALSE);
819 }
820
821 /* Spread out a narrow ASCII string into a wide-character string. */
822 static gunichar *
make_wide(const char * p)823 make_wide(const char *p)
824 {
825 gunichar *ret;
826 guint8 check;
827 int i;
828 ret = g_malloc((strlen(p) + 1) * sizeof(gunichar));
829 for (i = 0; p[i] != 0; i++) {
830 check = (guint8) p[i];
831 g_assert(check < 0x80);
832 ret[i] = check;
833 }
834 ret[i] = '\0';
835 return ret;
836 }
837
838 /* Print the contents of a GValueArray. */
839 static void
print_array(GValueArray * array)840 print_array(GValueArray *array)
841 {
842 int i;
843 GValue *value;
844 if (array != NULL) {
845 printf(" (");
846 for (i = 0; i < array->n_values; i++) {
847 value = g_value_array_get_nth(array, i);
848 if (i > 0) {
849 printf(", ");
850 }
851 if (G_VALUE_HOLDS_LONG(value)) {
852 printf("%ld", g_value_get_long(value));
853 } else
854 if (G_VALUE_HOLDS_STRING(value)) {
855 printf("\"%s\"", g_value_get_string(value));
856 } else
857 if (G_VALUE_HOLDS_POINTER(value)) {
858 printf("\"%ls\"",
859 (wchar_t*) g_value_get_pointer(value));
860 }
861 }
862 printf(")");
863 /* _vte_matcher_free_params_array(array); */
864 }
865 }
866
867 int
main(int argc,char ** argv)868 main(int argc, char **argv)
869 {
870 struct _vte_table *table;
871 int i;
872 const char *candidates[] = {
873 "ABCD",
874 "ABCDEF",
875 "]2;foo",
876 "]3;foo",
877 "]3;fook",
878 "[3;foo",
879 "[3;3m",
880 "[3;3mk",
881 "[3;3hk",
882 "[3;3h",
883 "]3;3h",
884 "[3;3k",
885 "[3;3kj",
886 "s",
887 };
888 const char *result, *p;
889 const gunichar *consumed;
890 char *tmp;
891 gunichar *candidate;
892 GQuark quark;
893 GValueArray *array;
894 g_type_init();
895 table = _vte_table_new();
896 _vte_table_add(table, "ABCDEFG", 7, "ABCDEFG", 0);
897 _vte_table_add(table, "ABCD", 4, "ABCD", 0);
898 _vte_table_add(table, "ABCDEFH", 7, "ABCDEFH", 0);
899 _vte_table_add(table, "ACDEFH", 6, "ACDEFH", 0);
900 _vte_table_add(table, "ACDEF%sJ", 8, "ACDEF%sJ", 0);
901 _vte_table_add(table, "ACDEF%i%mJ", 10, "ACDEF%dJ", 0);
902 _vte_table_add(table, "[%mh", 5, "move-cursor", 0);
903 _vte_table_add(table, "[%d;%d;%dm", 11, "set-graphic-rendition", 0);
904 _vte_table_add(table, "[%dm", 5, "set-graphic-rendition", 0);
905 _vte_table_add(table, "[m", 3, "set-graphic-rendition", 0);
906 _vte_table_add(table, "]3;%s", 7, "set-icon-title", 0);
907 _vte_table_add(table, "]4;%s", 7, "set-window-title", 0);
908 printf("Table contents:\n");
909 _vte_table_print(table);
910 printf("\nTable matches:\n");
911 for (i = 0; i < G_N_ELEMENTS(candidates); i++) {
912 p = candidates[i];
913 candidate = make_wide(p);
914 array = NULL;
915 _vte_table_match(table, candidate, strlen(p),
916 &result, &consumed, &quark, &array);
917 tmp = escape(p);
918 printf("`%s' => `%s'", tmp, (result ? result : "(NULL)"));
919 g_free(tmp);
920 print_array(array);
921 printf(" (%d chars)\n", (int) (consumed ? consumed - candidate: 0));
922 g_free(candidate);
923 }
924 _vte_table_free(table);
925 return 0;
926 }
927 #endif
928
929 const struct _vte_matcher_class _vte_matcher_table = {
930 (_vte_matcher_create_func)_vte_table_new,
931 (_vte_matcher_add_func)_vte_table_add,
932 (_vte_matcher_print_func)_vte_table_print,
933 (_vte_matcher_match_func)_vte_table_match,
934 (_vte_matcher_destroy_func)_vte_table_free
935 };
936