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 (&params);
674 
675 	/* Check for a pattern match. */
676 	ret = _vte_table_matchi(table, candidate, length,
677 				res, consumed, quark,
678 				&original, &original_length,
679 				&params);
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 (&params);
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 (&params);
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 		"oo",
879 		"",
880 		"k",
881 		"k",
882 		"",
883 		"]3;3h",
884 		"",
885 		"j",
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, "", 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