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