xref: /PHP-8.1/sapi/phpdbg/phpdbg_cmd.c (revision aff36587)
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 "phpdbg.h"
20 #include "phpdbg_cmd.h"
21 #include "phpdbg_utils.h"
22 #include "phpdbg_set.h"
23 #include "phpdbg_prompt.h"
24 #include "phpdbg_io.h"
25 
ZEND_EXTERN_MODULE_GLOBALS(phpdbg)26 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
27 
28 static inline const char *phpdbg_command_name(const phpdbg_command_t *command, char *buffer) {
29 	size_t pos = 0;
30 
31 	if (command->parent) {
32 		memcpy(&buffer[pos], command->parent->name, command->parent->name_len);
33 		pos += command->parent->name_len;
34 		memcpy(&buffer[pos], " ", sizeof(" ")-1);
35 		pos += (sizeof(" ")-1);
36 	}
37 
38 	memcpy(&buffer[pos], command->name, command->name_len);
39 	pos += command->name_len;
40 	buffer[pos] = 0;
41 
42 	return buffer;
43 }
44 
phpdbg_get_param_type(const phpdbg_param_t * param)45 PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param) /* {{{ */
46 {
47 	switch (param->type) {
48 		case STACK_PARAM:
49 			return "stack";
50 		case EMPTY_PARAM:
51 			return "empty";
52 		case ADDR_PARAM:
53 			return "address";
54 		case NUMERIC_PARAM:
55 			return "numeric";
56 		case METHOD_PARAM:
57 			return "method";
58 		case NUMERIC_FUNCTION_PARAM:
59 			return "function opline";
60 		case NUMERIC_METHOD_PARAM:
61 			return "method opline";
62 		case FILE_PARAM:
63 			return "file or file opline";
64 		case STR_PARAM:
65 			return "string";
66 		default: /* this is bad */
67 			return "unknown";
68 	}
69 }
70 
phpdbg_clear_param(phpdbg_param_t * param)71 PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param) /* {{{ */
72 {
73 	if (param) {
74 		switch (param->type) {
75 			case FILE_PARAM:
76 				efree(param->file.name);
77 				break;
78 			case METHOD_PARAM:
79 				efree(param->method.class);
80 				efree(param->method.name);
81 				break;
82 			case STR_PARAM:
83 				efree(param->str);
84 				break;
85 			default:
86 				break;
87 		}
88 	}
89 
90 } /* }}} */
91 
phpdbg_param_tostring(const phpdbg_param_t * param,char ** pointer)92 PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer) /* {{{ */
93 {
94 	switch (param->type) {
95 		case STR_PARAM:
96 			ZEND_IGNORE_VALUE(asprintf(pointer, "%s", param->str));
97 		break;
98 
99 		case ADDR_PARAM:
100 			ZEND_IGNORE_VALUE(asprintf(pointer, ZEND_ULONG_FMT, param->addr));
101 		break;
102 
103 		case NUMERIC_PARAM:
104 			ZEND_IGNORE_VALUE(asprintf(pointer, ZEND_LONG_FMT, param->num));
105 		break;
106 
107 		case METHOD_PARAM:
108 			ZEND_IGNORE_VALUE(asprintf(pointer, "%s::%s", param->method.class, param->method.name));
109 		break;
110 
111 		case FILE_PARAM:
112 			if (param->num) {
113 				ZEND_IGNORE_VALUE(asprintf(pointer, "%s:"ZEND_ULONG_FMT"#"ZEND_ULONG_FMT, param->file.name, param->file.line, param->num));
114 			} else {
115 				ZEND_IGNORE_VALUE(asprintf(pointer, "%s:"ZEND_ULONG_FMT, param->file.name, param->file.line));
116 			}
117 		break;
118 
119 		case NUMERIC_FUNCTION_PARAM:
120 			ZEND_IGNORE_VALUE(asprintf(pointer, "%s#"ZEND_ULONG_FMT, param->str, param->num));
121 		break;
122 
123 		case NUMERIC_METHOD_PARAM:
124 			ZEND_IGNORE_VALUE(asprintf(pointer, "%s::%s#"ZEND_ULONG_FMT, param->method.class, param->method.name, param->num));
125 		break;
126 
127 		default:
128 			*pointer = strdup("unknown");
129 	}
130 
131 	return *pointer;
132 } /* }}} */
133 
phpdbg_copy_param(const phpdbg_param_t * src,phpdbg_param_t * dest)134 PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest) /* {{{ */
135 {
136 	switch ((dest->type = src->type)) {
137 		case STACK_PARAM:
138 			/* nope */
139 		break;
140 
141 		case STR_PARAM:
142 			dest->str = estrndup(src->str, src->len);
143 			dest->len = src->len;
144 		break;
145 
146 		case OP_PARAM:
147 			dest->str = estrndup(src->str, src->len);
148 			dest->len = src->len;
149 		break;
150 
151 		case ADDR_PARAM:
152 			dest->addr = src->addr;
153 		break;
154 
155 		case NUMERIC_PARAM:
156 			dest->num = src->num;
157 		break;
158 
159 		case METHOD_PARAM:
160 			dest->method.class = estrdup(src->method.class);
161 			dest->method.name = estrdup(src->method.name);
162 		break;
163 
164 		case NUMERIC_FILE_PARAM:
165 		case FILE_PARAM:
166 			dest->file.name = estrdup(src->file.name);
167 			dest->file.line = src->file.line;
168 			if (src->num)
169 				dest->num   = src->num;
170 		break;
171 
172 		case NUMERIC_FUNCTION_PARAM:
173 			dest->str = estrndup(src->str, src->len);
174 			dest->num = src->num;
175 			dest->len = src->len;
176 		break;
177 
178 		case NUMERIC_METHOD_PARAM:
179 			dest->method.class = estrdup(src->method.class);
180 			dest->method.name = estrdup(src->method.name);
181 			dest->num = src->num;
182 		break;
183 
184 		case EMPTY_PARAM: { /* do nothing */ } break;
185 
186 		default: {
187 			/* not yet */
188 		}
189 	}
190 } /* }}} */
191 
phpdbg_hash_param(const phpdbg_param_t * param)192 PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param) /* {{{ */
193 {
194 	zend_ulong hash = param->type;
195 
196 	switch (param->type) {
197 		case STACK_PARAM:
198 			/* nope */
199 		break;
200 
201 		case STR_PARAM:
202 			hash += zend_hash_func(param->str, param->len);
203 		break;
204 
205 		case METHOD_PARAM:
206 			hash += zend_hash_func(param->method.class, strlen(param->method.class));
207 			hash += zend_hash_func(param->method.name, strlen(param->method.name));
208 		break;
209 
210 		case FILE_PARAM:
211 			hash += zend_hash_func(param->file.name, strlen(param->file.name));
212 			hash += param->file.line;
213 			if (param->num)
214 				hash += param->num;
215 		break;
216 
217 		case ADDR_PARAM:
218 			hash += param->addr;
219 		break;
220 
221 		case NUMERIC_PARAM:
222 			hash += param->num;
223 		break;
224 
225 		case NUMERIC_FUNCTION_PARAM:
226 			hash += zend_hash_func(param->str, param->len);
227 			hash += param->num;
228 		break;
229 
230 		case NUMERIC_METHOD_PARAM:
231 			hash += zend_hash_func(param->method.class, strlen(param->method.class));
232 			hash += zend_hash_func(param->method.name, strlen(param->method.name));
233 			if (param->num)
234 				hash+= param->num;
235 		break;
236 
237 		case EMPTY_PARAM: { /* do nothing */ } break;
238 
239 		default: {
240 			/* not yet */
241 		}
242 	}
243 
244 	return hash;
245 } /* }}} */
246 
phpdbg_match_param(const phpdbg_param_t * l,const phpdbg_param_t * r)247 PHPDBG_API bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r) /* {{{ */
248 {
249 	if (l && r) {
250 		if (l->type == r->type) {
251 			switch (l->type) {
252 				case STACK_PARAM:
253 					/* nope, or yep */
254 					return 1;
255 				break;
256 
257 				case NUMERIC_FUNCTION_PARAM:
258 					if (l->num != r->num) {
259 						break;
260 					}
261 				ZEND_FALLTHROUGH;
262 
263 				case STR_PARAM:
264 					return (l->len == r->len) &&
265 							(memcmp(l->str, r->str, l->len) == SUCCESS);
266 
267 				case NUMERIC_PARAM:
268 					return (l->num == r->num);
269 
270 				case ADDR_PARAM:
271 					return (l->addr == r->addr);
272 
273 				case FILE_PARAM: {
274 					if (l->file.line == r->file.line) {
275 						size_t lengths[2] = {
276 							strlen(l->file.name), strlen(r->file.name)};
277 
278 						if (lengths[0] == lengths[1]) {
279 							if ((!l->num && !r->num) || (l->num == r->num)) {
280 								return (memcmp(
281 									l->file.name, r->file.name, lengths[0]) == SUCCESS);
282 							}
283 						}
284 					}
285 				} break;
286 
287 				case NUMERIC_METHOD_PARAM:
288 					if (l->num != r->num) {
289 						break;
290 					}
291 				ZEND_FALLTHROUGH;
292 
293 				case METHOD_PARAM: {
294 					size_t lengths[2] = {
295 						strlen(l->method.class), strlen(r->method.class)};
296 					if (lengths[0] == lengths[1]) {
297 						if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) {
298 							lengths[0] = strlen(l->method.name);
299 							lengths[1] = strlen(r->method.name);
300 
301 							if (lengths[0] == lengths[1]) {
302 								return (memcmp(
303 									l->method.name, r->method.name, lengths[0]) == SUCCESS);
304 							}
305 						}
306 					}
307 				} break;
308 
309 				case EMPTY_PARAM:
310 					return 1;
311 
312 				default: {
313 					/* not yet */
314 				}
315 			}
316 		}
317 	}
318 	return 0;
319 } /* }}} */
320 
321 /* {{{ */
phpdbg_param_debug(const phpdbg_param_t * param,const char * msg)322 PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) {
323 	if (param && param->type) {
324 		switch (param->type) {
325 			case STR_PARAM:
326 				fprintf(stderr, "%s STR_PARAM(%s=%zu)\n", msg, param->str, param->len);
327 			break;
328 
329 			case ADDR_PARAM:
330 				fprintf(stderr, "%s ADDR_PARAM(" ZEND_ULONG_FMT ")\n", msg, param->addr);
331 			break;
332 
333 			case NUMERIC_FILE_PARAM:
334 				fprintf(stderr, "%s NUMERIC_FILE_PARAM(%s:#"ZEND_ULONG_FMT")\n", msg, param->file.name, param->file.line);
335 			break;
336 
337 			case FILE_PARAM:
338 				fprintf(stderr, "%s FILE_PARAM(%s:"ZEND_ULONG_FMT")\n", msg, param->file.name, param->file.line);
339 			break;
340 
341 			case METHOD_PARAM:
342 				fprintf(stderr, "%s METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
343 			break;
344 
345 			case NUMERIC_METHOD_PARAM:
346 				fprintf(stderr, "%s NUMERIC_METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
347 			break;
348 
349 			case NUMERIC_FUNCTION_PARAM:
350 				fprintf(stderr, "%s NUMERIC_FUNCTION_PARAM(%s::"ZEND_LONG_FMT")\n", msg, param->str, param->num);
351 			break;
352 
353 			case NUMERIC_PARAM:
354 				fprintf(stderr, "%s NUMERIC_PARAM("ZEND_LONG_FMT")\n", msg, param->num);
355 			break;
356 
357 			case COND_PARAM:
358 				fprintf(stderr, "%s COND_PARAM(%s=%zu)\n", msg, param->str, param->len);
359 			break;
360 
361 			case OP_PARAM:
362 				fprintf(stderr, "%s OP_PARAM(%s=%zu)\n", msg, param->str, param->len);
363 			break;
364 
365 			default: {
366 				/* not yet */
367 			}
368 		}
369 	}
370 } /* }}} */
371 
372 /* {{{ */
phpdbg_stack_free(phpdbg_param_t * stack)373 PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack) {
374 	if (stack && stack->next) {
375 		phpdbg_param_t *remove = stack->next;
376 
377 		while (remove) {
378 			phpdbg_param_t *next = NULL;
379 
380 			if (remove->next)
381 				next = remove->next;
382 
383 			switch (remove->type) {
384 				case NUMERIC_METHOD_PARAM:
385 				case METHOD_PARAM:
386 					if (remove->method.class) {
387 						efree(remove->method.class);
388 					}
389 					if (remove->method.name) {
390 						efree(remove->method.name);
391 					}
392 				break;
393 
394 				case NUMERIC_FUNCTION_PARAM:
395 				case STR_PARAM:
396 				case OP_PARAM:
397 				case EVAL_PARAM:
398 				case SHELL_PARAM:
399 				case COND_PARAM:
400 				case RUN_PARAM:
401 					if (remove->str) {
402 						efree(remove->str);
403 					}
404 				break;
405 
406 				case NUMERIC_FILE_PARAM:
407 				case FILE_PARAM:
408 					if (remove->file.name) {
409 						efree(remove->file.name);
410 					}
411 				break;
412 
413 				default: {
414 					/* nothing */
415 				}
416 			}
417 
418 			free(remove);
419 			remove = NULL;
420 
421 			if (next)
422 				remove = next;
423 			else break;
424 		}
425 	}
426 
427 
428 	stack->next = NULL;
429 } /* }}} */
430 
431 /* {{{ */
phpdbg_stack_push(phpdbg_param_t * stack,phpdbg_param_t * param)432 PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) {
433 	phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t));
434 
435 	if (!next) {
436 		return;
437 	}
438 
439 	*(next) = *(param);
440 
441 	next->next = NULL;
442 
443 	if (stack->top == NULL) {
444 		stack->top = next;
445 		next->top = NULL;
446 		stack->next = next;
447 	} else {
448 		stack->top->next = next;
449 		next->top = stack->top;
450 		stack->top = next;
451 	}
452 
453 	stack->len++;
454 } /* }}} */
455 
456 /* {{{ */
phpdbg_stack_separate(phpdbg_param_t * param)457 PHPDBG_API void phpdbg_stack_separate(phpdbg_param_t *param) {
458 	phpdbg_param_t *stack = calloc(1, sizeof(phpdbg_param_t));
459 
460 	stack->type = STACK_PARAM;
461 	stack->next = param->next;
462 	param->next = stack;
463 	stack->top = param->top;
464 } /* }}} */
465 
phpdbg_stack_verify(const phpdbg_command_t * command,phpdbg_param_t ** stack)466 PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack) {
467 	if (command) {
468 		char buffer[128] = {0,};
469 		const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL;
470 		const char *arg = command->args;
471 		zend_ulong least = 0L,
472 		           received = 0L,
473 		           current = 0L;
474 		bool optional = 0;
475 
476 		/* check for arg spec */
477 		if (!(arg) || !(*arg)) {
478 			if (!top || top->type == STACK_PARAM) {
479 				return SUCCESS;
480 			}
481 
482 			phpdbg_error("The command \"%s\" expected no arguments",
483 				phpdbg_command_name(command, buffer));
484 			return FAILURE;
485 		}
486 
487 		least = 0L;
488 
489 		/* count least amount of arguments */
490 		while (arg && *arg) {
491 			if (arg[0] == '|') {
492 				break;
493 			}
494 			least++;
495 			arg++;
496 		}
497 
498 		arg = command->args;
499 
500 #define verify_arg(e, a, t) if (!(a)) { \
501 	if (!optional) { \
502 		phpdbg_error("The command \"%s\" expected %s and got nothing at parameter "ZEND_ULONG_FMT, \
503 			phpdbg_command_name(command, buffer), \
504 			(e), \
505 			current); \
506 		return FAILURE;\
507 	} \
508 } else if ((a)->type != (t)) { \
509 	phpdbg_error("The command \"%s\" expected %s and got %s at parameter "ZEND_ULONG_FMT, \
510 		phpdbg_command_name(command, buffer), \
511 		(e),\
512 		phpdbg_get_param_type((a)), \
513 		current); \
514 	return FAILURE; \
515 }
516 
517 		while (arg && *arg) {
518 			if (top && top->type == STACK_PARAM) {
519 				break;
520 			}
521 
522 			current++;
523 
524 			switch (*arg) {
525 				case '|': {
526 					current--;
527 					optional = 1;
528 					arg++;
529 				} continue;
530 
531 				case 'i': verify_arg("raw input", top, STR_PARAM); break;
532 				case 's': verify_arg("string", top, STR_PARAM); break;
533 				case 'n': verify_arg("number", top, NUMERIC_PARAM); break;
534 				case 'm': verify_arg("method", top, METHOD_PARAM); break;
535 				case 'a': verify_arg("address", top, ADDR_PARAM); break;
536 				case 'f': verify_arg("file:line", top, FILE_PARAM); break;
537 				case 'c': verify_arg("condition", top, COND_PARAM); break;
538 				case 'o': verify_arg("opcode", top, OP_PARAM); break;
539 				case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break;
540 
541 				case '*': { /* do nothing */ } break;
542 			}
543 
544 			if (top) {
545 				top = top->next;
546 			} else {
547 				break;
548 			}
549 
550 			received++;
551 			arg++;
552 		}
553 
554 #undef verify_arg
555 
556 		if ((received < least)) {
557 			phpdbg_error("The command \"%s\" expected at least "ZEND_ULONG_FMT" arguments (%s) and received "ZEND_ULONG_FMT,
558 				phpdbg_command_name(command, buffer),
559 				least,
560 				command->args,
561 				received);
562 			return FAILURE;
563 		}
564 	}
565 
566 	return SUCCESS;
567 }
568 
569 /* {{{ */
phpdbg_stack_resolve(const phpdbg_command_t * commands,const phpdbg_command_t * parent,phpdbg_param_t ** top)570 PHPDBG_API const phpdbg_command_t *phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top) {
571 	const phpdbg_command_t *command = commands;
572 	phpdbg_param_t *name = *top;
573 	const phpdbg_command_t *matched[3] = {NULL, NULL, NULL};
574 	zend_ulong matches = 0L;
575 
576 	while (command && command->name && command->handler) {
577 		if (name->len == 1 || command->name_len >= name->len) {
578 			/* match single letter alias */
579 			if (command->alias && (name->len == 1)) {
580 				if (command->alias == (*name->str)) {
581 					matched[matches] = command;
582 					matches++;
583 				}
584 			} else {
585 				/* match full, case insensitive, command name */
586 				if (strncasecmp(command->name, name->str, name->len) == SUCCESS) {
587 					if (matches < 3) {
588 						/* only allow abbreviating commands that can be aliased */
589 						if ((name->len != command->name_len && command->alias) || name->len == command->name_len) {
590 							matched[matches] = command;
591 							matches++;
592 						}
593 
594 						/* exact match */
595 						if (name->len == command->name_len) {
596 							break;
597 						}
598 					} else {
599 						break;
600 					}
601 				}
602 			}
603 		}
604 
605 		command++;
606 	}
607 
608 	switch (matches) {
609 		case 0:
610 			if (parent) {
611 				phpdbg_error("The command \"%s %s\" could not be found", parent->name, name->str);
612 			} else {
613 				phpdbg_error("The command \"%s\" could not be found", name->str);
614 			}
615 			return parent;
616 
617 		case 1:
618 			(*top) = (*top)->next;
619 
620 			command = matched[0];
621 			break;
622 
623 		default: {
624 			char *list = NULL;
625 			uint32_t it = 0;
626 			size_t pos = 0;
627 
628 			while (it < matches) {
629 				if (!list) {
630 					list = emalloc(matched[it]->name_len + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0));
631 				} else {
632 					list = erealloc(list, (pos + matched[it]->name_len) + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0));
633 				}
634 				memcpy(&list[pos], matched[it]->name, matched[it]->name_len);
635 				pos += matched[it]->name_len;
636 				if ((it + 1) < matches) {
637 					memcpy(&list[pos], ", ", sizeof(", ") - 1);
638 					pos += (sizeof(", ") - 1);
639 				}
640 
641 				list[pos] = 0;
642 				it++;
643 			}
644 
645 			/* ", " separated matches */
646 			phpdbg_error("The command \"%s\" is ambiguous, matching "ZEND_ULONG_FMT" commands (%s)", name->str, matches, list);
647 			efree(list);
648 
649 			return NULL;
650 		}
651 	}
652 
653 	if (command->subs && (*top) && ((*top)->type == STR_PARAM)) {
654 		return phpdbg_stack_resolve(command->subs, command, top);
655 	} else {
656 		return command;
657 	}
658 
659 	return NULL;
660 } /* }}} */
661 
phpdbg_internal_stack_execute(phpdbg_param_t * stack,bool allow_async_unsafe)662 static int phpdbg_internal_stack_execute(phpdbg_param_t *stack, bool allow_async_unsafe) {
663 	const phpdbg_command_t *handler = NULL;
664 	phpdbg_param_t *top = (phpdbg_param_t *) stack->next;
665 
666 	switch (top->type) {
667 		case EVAL_PARAM:
668 			phpdbg_activate_err_buf(0);
669 			phpdbg_free_err_buf();
670 			return PHPDBG_COMMAND_HANDLER(ev)(top);
671 
672 		case RUN_PARAM:
673 			if (!allow_async_unsafe) {
674 				phpdbg_error("run command is disallowed during hard interrupt");
675 			}
676 			phpdbg_activate_err_buf(0);
677 			phpdbg_free_err_buf();
678 			return PHPDBG_COMMAND_HANDLER(run)(top);
679 
680 		case SHELL_PARAM:
681 			if (!allow_async_unsafe) {
682 				phpdbg_error("sh command is disallowed during hard interrupt");
683 				return FAILURE;
684 			}
685 			phpdbg_activate_err_buf(0);
686 			phpdbg_free_err_buf();
687 			return PHPDBG_COMMAND_HANDLER(sh)(top);
688 
689 		case STR_PARAM: {
690 			handler = phpdbg_stack_resolve(phpdbg_prompt_commands, NULL, &top);
691 
692 			if (handler) {
693 				if (!allow_async_unsafe && !(handler->flags & PHPDBG_ASYNC_SAFE)) {
694 					phpdbg_error("%s command is disallowed during hard interrupt", handler->name);
695 					return FAILURE;
696 				}
697 
698 				if (phpdbg_stack_verify(handler, &top) == SUCCESS) {
699 					phpdbg_activate_err_buf(0);
700 					phpdbg_free_err_buf();
701 					return handler->handler(top);
702 				}
703 			}
704 		} return FAILURE;
705 
706 		default:
707 			phpdbg_error("The first parameter makes no sense !");
708 			return FAILURE;
709 	}
710 
711 	return SUCCESS;
712 } /* }}} */
713 
714 /* {{{ */
phpdbg_stack_execute(phpdbg_param_t * stack,bool allow_async_unsafe)715 PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, bool allow_async_unsafe) {
716 	phpdbg_param_t *top = stack;
717 
718 	if (stack->type != STACK_PARAM) {
719 		phpdbg_error("The passed argument was not a stack !");
720 		return FAILURE;
721 	}
722 
723 	if (!stack->len) {
724 		phpdbg_error("The stack contains nothing !");
725 		return FAILURE;
726 	}
727 
728 	do {
729 		if (top->type == STACK_PARAM) {
730 			int result;
731 			if ((result = phpdbg_internal_stack_execute(top, allow_async_unsafe)) != SUCCESS) {
732 				return result;
733 			}
734 		}
735 	} while ((top = top->next));
736 
737 	return SUCCESS;
738 } /* }}} */
739 
phpdbg_read_input(const char * buffered)740 PHPDBG_API char *phpdbg_read_input(const char *buffered) /* {{{ */
741 {
742 	char buf[PHPDBG_MAX_CMD];
743 	char *buffer = NULL;
744 
745 	if ((PHPDBG_G(flags) & (PHPDBG_IS_STOPPING | PHPDBG_IS_RUNNING)) != PHPDBG_IS_STOPPING) {
746 		if (buffered == NULL) {
747 #ifdef HAVE_PHPDBG_READLINE
748 			char *cmd = readline(phpdbg_get_prompt());
749 			PHPDBG_G(last_was_newline) = 1;
750 
751 			if (!cmd) {
752 				PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
753 				zend_bailout();
754 			}
755 
756 			add_history(cmd);
757 			buffer = estrdup(cmd);
758 			free(cmd);
759 #else
760 			phpdbg_write("%s", phpdbg_get_prompt());
761 			phpdbg_consume_stdin_line(buf);
762 			buffer = estrdup(buf);
763 #endif
764 		} else {
765 			buffer = estrdup(buffered);
766 		}
767 	}
768 
769 	if (buffer && isspace(*buffer)) {
770 		char *trimmed = buffer;
771 		while (isspace(*trimmed))
772 			trimmed++;
773 
774 		trimmed = estrdup(trimmed);
775 		efree(buffer);
776 		buffer = trimmed;
777 	}
778 
779 	if (buffer && strlen(buffer)) {
780 		if (PHPDBG_G(buffer)) {
781 			free(PHPDBG_G(buffer));
782 		}
783 		PHPDBG_G(buffer) = strdup(buffer);
784 	} else if (PHPDBG_G(buffer)) {
785 		if (buffer) {
786 			efree(buffer);
787 		}
788 		buffer = estrdup(PHPDBG_G(buffer));
789 	}
790 
791 	return buffer;
792 } /* }}} */
793 
phpdbg_destroy_input(char ** input)794 PHPDBG_API void phpdbg_destroy_input(char **input) /*{{{ */
795 {
796 	efree(*input);
797 } /* }}} */
798 
phpdbg_ask_user_permission(const char * question)799 PHPDBG_API int phpdbg_ask_user_permission(const char *question) {
800 	char buf[PHPDBG_MAX_CMD];
801 	phpdbg_out("%s", question);
802 	phpdbg_out(" (type y or n): ");
803 
804 	while (1) {
805 		phpdbg_consume_stdin_line(buf);
806 		if ((buf[1] == '\n' || (buf[1] == '\r' && buf[2] == '\n')) && (buf[0] == 'y' || buf[0] == 'n')) {
807 			if (buf[0] == 'y') {
808 				return SUCCESS;
809 			}
810 			return FAILURE;
811 		}
812 		phpdbg_out("Please enter either y (yes) or n (no): ");
813 	}
814 
815 	return SUCCESS;
816 }
817