xref: /php-src/sapi/phpdbg/phpdbg_utils.c (revision 4107cb2e)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Felipe Pena <felipe@php.net>                                |
14    | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
15    | Authors: Bob Weinand <bwoebi@php.net>                                |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "zend.h"
20 
21 #include "php.h"
22 #include "phpdbg.h"
23 #include "phpdbg_utils.h"
24 #include "ext/standard/php_string.h"
25 
26 /* FASYNC under Solaris */
27 #ifdef HAVE_SYS_FILE_H
28 # include <sys/file.h>
29 #endif
30 
31 #ifdef HAVE_SYS_IOCTL_H
32 # include "sys/ioctl.h"
33 # ifndef GWINSZ_IN_SYS_IOCTL
34 #  include <termios.h>
35 # endif
36 #endif
37 
38 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
39 
40 /* {{{ color structures */
41 static const phpdbg_color_t colors[] = {
42 	PHPDBG_COLOR_D("none",             "0;0"),
43 
44 	PHPDBG_COLOR_D("white",            "0;64"),
45 	PHPDBG_COLOR_D("white-bold",       "1;64"),
46 	PHPDBG_COLOR_D("white-underline",  "4;64"),
47 	PHPDBG_COLOR_D("red",              "0;31"),
48 	PHPDBG_COLOR_D("red-bold",         "1;31"),
49 	PHPDBG_COLOR_D("red-underline",    "4;31"),
50 	PHPDBG_COLOR_D("green",            "0;32"),
51 	PHPDBG_COLOR_D("green-bold",       "1;32"),
52 	PHPDBG_COLOR_D("green-underline",  "4;32"),
53 	PHPDBG_COLOR_D("yellow",           "0;33"),
54 	PHPDBG_COLOR_D("yellow-bold",      "1;33"),
55 	PHPDBG_COLOR_D("yellow-underline", "4;33"),
56 	PHPDBG_COLOR_D("blue",             "0;34"),
57 	PHPDBG_COLOR_D("blue-bold",        "1;34"),
58 	PHPDBG_COLOR_D("blue-underline",   "4;34"),
59 	PHPDBG_COLOR_D("purple",           "0;35"),
60 	PHPDBG_COLOR_D("purple-bold",      "1;35"),
61 	PHPDBG_COLOR_D("purple-underline", "4;35"),
62 	PHPDBG_COLOR_D("cyan",             "0;36"),
63 	PHPDBG_COLOR_D("cyan-bold",        "1;36"),
64 	PHPDBG_COLOR_D("cyan-underline",   "4;36"),
65 	PHPDBG_COLOR_D("black",            "0;30"),
66 	PHPDBG_COLOR_D("black-bold",       "1;30"),
67 	PHPDBG_COLOR_D("black-underline",  "4;30"),
68 	PHPDBG_COLOR_END
69 }; /* }}} */
70 
71 /* {{{ */
72 static const phpdbg_element_t elements[] = {
73 	PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT),
74 	PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR),
75 	PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE),
76 	PHPDBG_ELEMENT_END
77 }; /* }}} */
78 
phpdbg_is_numeric(const char * str)79 PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */
80 {
81 	if (!str)
82 		return 0;
83 
84 	for (; *str; str++) {
85 		if (isspace(*str) || *str == '-') {
86 			continue;
87 		}
88 		return isdigit(*str);
89 	}
90 	return 0;
91 } /* }}} */
92 
phpdbg_is_empty(const char * str)93 PHPDBG_API int phpdbg_is_empty(const char *str) /* {{{ */
94 {
95 	if (!str)
96 		return 1;
97 
98 	for (; *str; str++) {
99 		if (isspace(*str)) {
100 			continue;
101 		}
102 		return 0;
103 	}
104 	return 1;
105 } /* }}} */
106 
phpdbg_is_addr(const char * str)107 PHPDBG_API int phpdbg_is_addr(const char *str) /* {{{ */
108 {
109 	return str[0] && str[1] && memcmp(str, "0x", 2) == 0;
110 } /* }}} */
111 
phpdbg_is_class_method(const char * str,size_t len,char ** class,char ** method)112 PHPDBG_API int phpdbg_is_class_method(const char *str, size_t len, char **class, char **method) /* {{{ */
113 {
114 	char *sep = NULL;
115 
116 	if (strstr(str, "#") != NULL)
117 		return 0;
118 
119 	if (strstr(str, " ") != NULL)
120 		return 0;
121 
122 	sep = strstr(str, "::");
123 
124 	if (!sep || sep == str || sep+2 == str+len-1) {
125 		return 0;
126 	}
127 
128 	if (class != NULL) {
129 
130 		if (str[0] == '\\') {
131 			str++;
132 			len--;
133 		}
134 
135 		*class = estrndup(str, sep - str);
136 		(*class)[sep - str] = 0;
137 	}
138 
139 	if (method != NULL) {
140 		*method = estrndup(sep+2, str + len - (sep + 2));
141 	}
142 
143 	return 1;
144 } /* }}} */
145 
phpdbg_resolve_path(const char * path)146 PHPDBG_API char *phpdbg_resolve_path(const char *path) /* {{{ */
147 {
148 	char resolved_name[MAXPATHLEN];
149 
150 	if (expand_filepath(path, resolved_name) == NULL) {
151 		return NULL;
152 	}
153 
154 	return strdup(resolved_name);
155 } /* }}} */
156 
phpdbg_current_file(void)157 PHPDBG_API const char *phpdbg_current_file(void) /* {{{ */
158 {
159 	const char *file = zend_get_executed_filename();
160 
161 	if (memcmp(file, "[no active file]", sizeof("[no active file]")) == 0) {
162 		return PHPDBG_G(exec);
163 	}
164 
165 	return file;
166 } /* }}} */
167 
phpdbg_get_function(const char * fname,const char * cname)168 PHPDBG_API const zend_function *phpdbg_get_function(const char *fname, const char *cname) /* {{{ */
169 {
170 	zend_function *func = NULL;
171 	zend_string *lfname = zend_string_init(fname, strlen(fname), 0);
172 	zend_string *tmp = zend_string_tolower(lfname);
173 	zend_string_release(lfname);
174 	lfname = tmp;
175 
176 	if (cname) {
177 		zend_class_entry *ce;
178 		zend_string *lcname = zend_string_init(cname, strlen(cname), 0);
179 		tmp = zend_string_tolower(lcname);
180 		zend_string_release(lcname);
181 		lcname = tmp;
182 		ce = zend_lookup_class(lcname);
183 
184 		zend_string_release(lcname);
185 
186 		if (ce) {
187 			func = zend_hash_find_ptr(&ce->function_table, lfname);
188 		}
189 	} else {
190 		func = zend_hash_find_ptr(EG(function_table), lfname);
191 	}
192 
193 	zend_string_release(lfname);
194 	return func;
195 } /* }}} */
196 
phpdbg_trim(const char * str,size_t len,size_t * new_len)197 PHPDBG_API char *phpdbg_trim(const char *str, size_t len, size_t *new_len) /* {{{ */
198 {
199 	const char *p = str;
200 	char *new = NULL;
201 
202 	while (isspace(*p)) {
203 		++p;
204 		--len;
205 	}
206 
207 	while (*p && isspace(*(p + len -1))) {
208 		--len;
209 	}
210 
211 	if (len == 0) {
212 		new = estrndup("", sizeof(""));
213 		*new_len = 0;
214 	} else {
215 		new = estrndup(p, len);
216 		*(new + len) = '\0';
217 
218 		if (new_len) {
219 			*new_len = len;
220 		}
221 	}
222 
223 	return new;
224 
225 } /* }}} */
226 
phpdbg_get_color(const char * name,size_t name_length)227 PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length) /* {{{ */
228 {
229 	const phpdbg_color_t *color = colors;
230 
231 	while (color && color->name) {
232 		if (name_length == color->name_length &&
233 			memcmp(name, color->name, name_length) == SUCCESS) {
234 			phpdbg_debug("phpdbg_get_color(%s, %lu): %s", name, name_length, color->code);
235 			return color;
236 		}
237 		++color;
238 	}
239 
240 	phpdbg_debug("phpdbg_get_color(%s, %lu): failed", name, name_length);
241 
242 	return NULL;
243 } /* }}} */
244 
phpdbg_set_color(int element,const phpdbg_color_t * color)245 PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color) /* {{{ */
246 {
247 	PHPDBG_G(colors)[element] = color;
248 } /* }}} */
249 
phpdbg_set_color_ex(int element,const char * name,size_t name_length)250 PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length) /* {{{ */
251 {
252 	const phpdbg_color_t *color = phpdbg_get_color(name, name_length);
253 
254 	if (color) {
255 		phpdbg_set_color(element, color);
256 	} else PHPDBG_G(colors)[element] = colors;
257 } /* }}} */
258 
phpdbg_get_colors(void)259 PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(void) /* {{{ */
260 {
261 	return colors;
262 } /* }}} */
263 
phpdbg_get_element(const char * name,size_t len)264 PHPDBG_API int phpdbg_get_element(const char *name, size_t len) {
265 	const phpdbg_element_t *element = elements;
266 
267 	while (element && element->name) {
268 		if (len == element->name_length) {
269 			if (strncasecmp(name, element->name, len) == SUCCESS) {
270 				return element->id;
271 			}
272 		}
273 		element++;
274 	}
275 
276 	return PHPDBG_COLOR_INVALID;
277 }
278 
phpdbg_set_prompt(const char * prompt)279 PHPDBG_API void phpdbg_set_prompt(const char *prompt) /* {{{ */
280 {
281 	/* free formatted prompt */
282 	if (PHPDBG_G(prompt)[1]) {
283 		free(PHPDBG_G(prompt)[1]);
284 		PHPDBG_G(prompt)[1] = NULL;
285 	}
286 	/* free old prompt */
287 	if (PHPDBG_G(prompt)[0]) {
288 		free(PHPDBG_G(prompt)[0]);
289 		PHPDBG_G(prompt)[0] = NULL;
290 	}
291 
292 	/* copy new prompt */
293 	PHPDBG_G(prompt)[0] = strdup(prompt);
294 } /* }}} */
295 
phpdbg_get_prompt(void)296 PHPDBG_API const char *phpdbg_get_prompt(void) /* {{{ */
297 {
298 	/* find cached prompt */
299 	if (PHPDBG_G(prompt)[1]) {
300 		return PHPDBG_G(prompt)[1];
301 	}
302 
303 	uint32_t pos = 0,
304 			 end = strlen(PHPDBG_G(prompt)[0]);
305 	bool unicode_warned = false;
306 
307 	while (pos < end) {
308 		if (PHPDBG_G(prompt)[0][pos] & 0x80) {
309 			PHPDBG_G(prompt)[0][pos] = '?';
310 
311 			if (!unicode_warned) {
312 				zend_error(E_WARNING,
313 					"prompt contains unsupported unicode characters");
314 				unicode_warned = true;
315 			}
316 		}
317 		pos++;
318 	}
319 
320 	/* create cached prompt */
321 #ifndef HAVE_LIBEDIT
322 	/* TODO: libedit doesn't seems to support coloured prompt */
323 	if ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED)) {
324 		ZEND_IGNORE_VALUE(asprintf(&PHPDBG_G(prompt)[1], "\033[%sm%s\033[0m ",
325 			PHPDBG_G(colors)[PHPDBG_COLOR_PROMPT]->code,
326 			PHPDBG_G(prompt)[0]));
327 	} else
328 #endif
329 	{
330 		ZEND_IGNORE_VALUE(asprintf(&PHPDBG_G(prompt)[1], "%s ", PHPDBG_G(prompt)[0]));
331 	}
332 
333 	return PHPDBG_G(prompt)[1];
334 } /* }}} */
335 
phpdbg_rebuild_symtable(void)336 int phpdbg_rebuild_symtable(void) {
337 	if (!EG(current_execute_data) || !EG(current_execute_data)->func) {
338 		phpdbg_error("No active op array!");
339 		return FAILURE;
340 	}
341 
342 	if (!zend_rebuild_symbol_table()) {
343 		phpdbg_error("No active symbol table!");
344 		return FAILURE;
345 	}
346 
347 	return SUCCESS;
348 }
349 
phpdbg_get_terminal_width(void)350 PHPDBG_API uint32_t phpdbg_get_terminal_width(void) /* {{{ */
351 {
352 	uint32_t columns;
353 #ifdef _WIN32
354 	CONSOLE_SCREEN_BUFFER_INFO csbi;
355 
356 	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
357 	columns = (uint32_t) csbi.srWindow.Right - csbi.srWindow.Left + 1;
358 #elif defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ)
359 	struct winsize w;
360 
361 	columns = (uint32_t) ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 80;
362 #else
363 	columns = 80;
364 #endif
365 	return columns;
366 } /* }}} */
367 
phpdbg_get_terminal_height(void)368 PHPDBG_API uint32_t phpdbg_get_terminal_height(void) /* {{{ */
369 {
370 	uint32_t lines;
371 #ifdef _WIN32
372 	CONSOLE_SCREEN_BUFFER_INFO csbi;
373 
374 	if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
375 		lines = (uint32_t) csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
376 	} else {
377 		lines = 40;
378 	}
379 #elif defined(HAVE_SYS_IOCTL_H) && defined(TIOCGWINSZ)
380 	struct winsize w;
381 
382 	lines = (uint32_t) ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_row : 40;
383 #else
384 	lines = 40;
385 #endif
386 	return lines;
387 } /* }}} */
388 
phpdbg_set_async_io(int fd)389 PHPDBG_API void phpdbg_set_async_io(int fd) {
390 #if !defined(_WIN32) && defined(FASYNC)
391 	int flags;
392 	fcntl(STDIN_FILENO, F_SETOWN, getpid());
393 	flags = fcntl(STDIN_FILENO, F_GETFL);
394 	fcntl(STDIN_FILENO, F_SETFL, flags | FASYNC);
395 #endif
396 }
397 
phpdbg_safe_class_lookup(const char * name,int name_length,zend_class_entry ** ce)398 int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry **ce) {
399 	if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
400 		char *lc_name, *lc_free;
401 		int lc_length;
402 
403 		if (name == NULL || !name_length) {
404 			return FAILURE;
405 		}
406 
407 		lc_free = lc_name = emalloc(name_length + 1);
408 		zend_str_tolower_copy(lc_name, name, name_length);
409 		lc_length = name_length + 1;
410 
411 		if (lc_name[0] == '\\') {
412 			lc_name += 1;
413 			lc_length -= 1;
414 		}
415 
416 		phpdbg_try_access {
417 			*ce = zend_hash_str_find_ptr(EG(class_table), lc_name, lc_length);
418 		} phpdbg_catch_access {
419 			phpdbg_error("Could not fetch class %.*s, invalid data source", name_length, name);
420 		} phpdbg_end_try_access();
421 
422 		efree(lc_free);
423 	} else {
424 		zend_string *str_name = zend_string_init(name, name_length, 0);
425 		*ce = zend_lookup_class(str_name);
426 		efree(str_name);
427 	}
428 
429 	return *ce ? SUCCESS : FAILURE;
430 }
431 
phpdbg_get_property_key(char * key)432 char *phpdbg_get_property_key(char *key) {
433 	if (*key != 0) {
434 		return key;
435 	}
436 	return strchr(key + 1, 0) + 1;
437 }
438 
phpdbg_parse_variable_arg_wrapper(char * name,size_t len,char * keyname,size_t keylen,HashTable * parent,zval * zv,phpdbg_parse_var_func callback)439 static int phpdbg_parse_variable_arg_wrapper(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval *zv, phpdbg_parse_var_func callback) {
440 	return callback(name, len, keyname, keylen, parent, zv);
441 }
442 
phpdbg_parse_variable(char * input,size_t len,HashTable * parent,size_t i,phpdbg_parse_var_func callback,bool silent)443 PHPDBG_API int phpdbg_parse_variable(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_func callback, bool silent) {
444 	return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_parse_variable_arg_wrapper, NULL, silent, callback);
445 }
446 
phpdbg_parse_variable_with_arg(char * input,size_t len,HashTable * parent,size_t i,phpdbg_parse_var_with_arg_func callback,phpdbg_parse_var_with_arg_func step_cb,bool silent,void * arg)447 PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_with_arg_func callback, phpdbg_parse_var_with_arg_func step_cb, bool silent, void *arg) {
448 	int ret = FAILURE;
449 	bool new_index = 1;
450 	char *last_index = NULL;
451 	size_t index_len = 0;
452 	zval *zv;
453 
454 	if (len < 2 || *input != '$') {
455 		goto error;
456 	}
457 
458 	while (i++ < len) {
459 		if (i == len) {
460 			new_index = 1;
461 		} else {
462 			switch (input[i]) {
463 				case '[':
464 					new_index = 1;
465 					break;
466 				case ']':
467 					break;
468 				case '>':
469 					if (!last_index) {
470 						goto error;
471 					}
472 					if (last_index[index_len - 1] == '-') {
473 						new_index = 1;
474 						index_len--;
475 					}
476 					break;
477 
478 				default:
479 					if (new_index) {
480 						last_index = input + i;
481 						new_index = 0;
482 					}
483 					if (input[i - 1] == ']') {
484 						goto error;
485 					}
486 					index_len++;
487 			}
488 		}
489 
490 		if (new_index && index_len == 0) {
491 			zend_ulong numkey;
492 			zend_string *strkey;
493 			ZEND_HASH_FOREACH_KEY_VAL_IND(parent, numkey, strkey, zv) {
494 				if (i == len || (i == len - 1 && input[len - 1] == ']')) {
495 					char *key, *propkey;
496 					size_t namelen, keylen;
497 					char *name;
498 					char *keyname = estrndup(last_index, index_len);
499 					if (strkey) {
500 						key = ZSTR_VAL(strkey);
501 						keylen = ZSTR_LEN(strkey);
502 					} else {
503 						keylen = spprintf(&key, 0, ZEND_ULONG_FMT, numkey);
504 					}
505 					propkey = phpdbg_get_property_key(key);
506 					namelen = i + keylen + 2;
507 					name = emalloc(namelen);
508 					namelen = snprintf(name, namelen, "%.*s%.*s%s", (int) i, input, (int) (keylen - (propkey - key)), propkey, input[len - 1] == ']'?"]":"");
509 					if (!strkey) {
510 						efree(key);
511 					}
512 
513 					ret = callback(name, namelen, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
514 				} else retry_ref: if (Z_TYPE_P(zv) == IS_OBJECT) {
515 					if (step_cb) {
516 						char *name = estrndup(input, i);
517 						char *keyname = estrndup(last_index, index_len);
518 
519 						ret = step_cb(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
520 					}
521 
522 					phpdbg_parse_variable_with_arg(input, len, Z_OBJPROP_P(zv), i, callback, step_cb, silent, arg);
523 				} else if (Z_TYPE_P(zv) == IS_ARRAY) {
524 					if (step_cb) {
525 						char *name = estrndup(input, i);
526 						char *keyname = estrndup(last_index, index_len);
527 
528 						ret = step_cb(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
529 					}
530 
531 					phpdbg_parse_variable_with_arg(input, len, Z_ARRVAL_P(zv), i, callback, step_cb, silent, arg);
532 				} else if (Z_ISREF_P(zv)) {
533 					if (step_cb) {
534 						char *name = estrndup(input, i);
535 						char *keyname = estrndup(last_index, index_len);
536 
537 						ret = step_cb(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
538 					}
539 
540 					ZVAL_DEREF(zv);
541 					goto retry_ref;
542 				} else {
543 					/* Ignore silently */
544 				}
545 			} ZEND_HASH_FOREACH_END();
546 			return ret;
547 		} else if (new_index) {
548 			char last_chr = last_index[index_len];
549 			last_index[index_len] = 0;
550 			if (!(zv = zend_symtable_str_find(parent, last_index, index_len))) {
551 				if (!silent) {
552 					phpdbg_error("%.*s is undefined", (int) (input[i] == ']' ? i + 1 : i), input);
553 				}
554 				return FAILURE;
555 			}
556 			while (Z_TYPE_P(zv) == IS_INDIRECT) {
557 				zv = Z_INDIRECT_P(zv);
558 			}
559 
560 			last_index[index_len] = last_chr;
561 			if (i == len) {
562 				char *name = estrndup(input, i);
563 				char *keyname = estrndup(last_index, index_len);
564 
565 				ret = callback(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
566 			} else retry_ref_end: if (Z_TYPE_P(zv) == IS_OBJECT) {
567 				if (step_cb) {
568 					char *name = estrndup(input, i);
569 					char *keyname = estrndup(last_index, index_len);
570 
571 					ret = step_cb(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
572 				}
573 
574 				parent = Z_OBJPROP_P(zv);
575 			} else if (Z_TYPE_P(zv) == IS_ARRAY) {
576 				if (step_cb) {
577 					char *name = estrndup(input, i);
578 					char *keyname = estrndup(last_index, index_len);
579 
580 					ret = step_cb(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
581 				}
582 
583 				parent = Z_ARRVAL_P(zv);
584 			} else if (Z_ISREF_P(zv)) {
585 				if (step_cb) {
586 					char *name = estrndup(input, i);
587 					char *keyname = estrndup(last_index, index_len);
588 
589 					ret = step_cb(name, i, keyname, index_len, parent, zv, arg) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
590 				}
591 
592 				ZVAL_DEREF(zv);
593 				goto retry_ref_end;
594 			} else {
595 				phpdbg_error("%.*s is nor an array nor an object", (int) (input[i] == '>' ? i - 1 : i), input);
596 				return FAILURE;
597 			}
598 			index_len = 0;
599 		}
600 	}
601 
602 	return ret;
603 	error:
604 		phpdbg_error("Malformed input");
605 		return FAILURE;
606 }
607 
phpdbg_is_auto_global(char * name,int len)608 int phpdbg_is_auto_global(char *name, int len) {
609 	return zend_is_auto_global_str(name, len);
610 }
611 
phpdbg_check_caught_ex(zend_execute_data * execute_data,zend_object * exception)612 PHPDBG_API bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_object *exception) {
613 	const zend_op *op;
614 	zend_op *cur;
615 	uint32_t op_num, i;
616 	zend_op_array *op_array = &execute_data->func->op_array;
617 
618 	if (execute_data->opline >= EG(exception_op) && execute_data->opline < EG(exception_op) + 3 && EG(opline_before_exception)) {
619 		op = EG(opline_before_exception);
620 	} else {
621 		op = execute_data->opline;
622 	}
623 
624 	op_num = op - op_array->opcodes;
625 
626 	for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op <= op_num; i++) {
627 		uint32_t catch = op_array->try_catch_array[i].catch_op, finally = op_array->try_catch_array[i].finally_op;
628 		if (op_num <= catch || op_num <= finally) {
629 			if (finally) {
630 				return 1;
631 			}
632 
633 			cur = &op_array->opcodes[catch];
634 			while (1) {
635 				zend_class_entry *ce;
636 
637 				if (!(ce = CACHED_PTR(cur->extended_value & ~ZEND_LAST_CATCH))) {
638 					ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(cur, cur->op1)), Z_STR_P(RT_CONSTANT(cur, cur->op1) + 1), ZEND_FETCH_CLASS_NO_AUTOLOAD);
639 					CACHE_PTR(cur->extended_value & ~ZEND_LAST_CATCH, ce);
640 				}
641 
642 				if (ce == exception->ce || (ce && instanceof_function(exception->ce, ce))) {
643 					return 1;
644 				}
645 
646 				if (cur->extended_value & ZEND_LAST_CATCH) {
647 					return 0;
648 				}
649 
650 				cur = OP_JMP_ADDR(cur, cur->op2);
651 			}
652 
653 			return 0;
654 		}
655 	}
656 
657 	return op->opcode == ZEND_CATCH;
658 }
659 
phpdbg_short_zval_print(zval * zv,int maxlen)660 char *phpdbg_short_zval_print(zval *zv, int maxlen) /* {{{ */
661 {
662 	char *decode = NULL;
663 
664 	switch (Z_TYPE_P(zv)) {
665 		case IS_UNDEF:
666 			decode = estrdup("");
667 			break;
668 		case IS_NULL:
669 			decode = estrdup("null");
670 			break;
671 		case IS_FALSE:
672 			decode = estrdup("false");
673 			break;
674 		case IS_TRUE:
675 			decode = estrdup("true");
676 			break;
677 		case IS_LONG:
678 			spprintf(&decode, 0, ZEND_LONG_FMT, Z_LVAL_P(zv));
679 			break;
680 		case IS_DOUBLE:
681 			spprintf(&decode, 0, "%.*G", 14, Z_DVAL_P(zv));
682 
683 			/* Make sure it looks like a float */
684 			if (zend_finite(Z_DVAL_P(zv)) && !strchr(decode, '.')) {
685 				size_t len = strlen(decode);
686 				char *decode2 = emalloc(len + strlen(".0") + 1);
687 				memcpy(decode2, decode, len);
688 				decode2[len] = '.';
689 				decode2[len+1] = '0';
690 				decode2[len+2] = '\0';
691 				efree(decode);
692 				decode = decode2;
693 			}
694 			break;
695 		case IS_STRING: {
696 			int i;
697 			zend_string *str = php_addcslashes(Z_STR_P(zv), "\\\"\n\t\0", 5);
698 			for (i = 0; i < ZSTR_LEN(str); i++) {
699 				if (ZSTR_VAL(str)[i] < 32) {
700 					ZSTR_VAL(str)[i] = ' ';
701 				}
702 			}
703 			spprintf(&decode, 0, "\"%.*s\"%c",
704 				ZSTR_LEN(str) <= maxlen - 2 ? (int) ZSTR_LEN(str) : (maxlen - 3),
705 				ZSTR_VAL(str), ZSTR_LEN(str) <= maxlen - 2 ? 0 : '+');
706 			zend_string_release(str);
707 			} break;
708 		case IS_RESOURCE:
709 			spprintf(&decode, 0, "Rsrc #" ZEND_LONG_FMT, Z_RES_HANDLE_P(zv));
710 			break;
711 		case IS_ARRAY:
712 			spprintf(&decode, 0, "array(%d)", zend_hash_num_elements(Z_ARR_P(zv)));
713 			break;
714 		case IS_OBJECT: {
715 			zend_string *str = Z_OBJCE_P(zv)->name;
716 			spprintf(&decode, 0, "%.*s%c",
717 				ZSTR_LEN(str) <= maxlen ? (int) ZSTR_LEN(str) : maxlen - 1,
718 				ZSTR_VAL(str), ZSTR_LEN(str) <= maxlen ? 0 : '+');
719 			break;
720 		}
721 		case IS_CONSTANT_AST: {
722 			zend_ast *ast = Z_ASTVAL_P(zv);
723 
724 			if (ast->kind == ZEND_AST_CONSTANT
725 			 || ast->kind == ZEND_AST_CONSTANT_CLASS
726 			 || ast->kind == ZEND_AST_CLASS_CONST) {
727 				decode = estrdup("<constant>");
728 			} else {
729 				decode = estrdup("<ast>");
730 			}
731 			break;
732 		}
733 		default:
734 			spprintf(&decode, 0, "unknown type: %d", Z_TYPE_P(zv));
735 			break;
736 	}
737 
738 	return decode;
739 } /* }}} */
740