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 ZEND_ASSERT(stack != NULL);
379
380 if (stack->next) {
381 phpdbg_param_t *remove = stack->next;
382
383 while (remove) {
384 phpdbg_param_t *next = NULL;
385
386 if (remove->next)
387 next = remove->next;
388
389 switch (remove->type) {
390 case NUMERIC_METHOD_PARAM:
391 case METHOD_PARAM:
392 if (remove->method.class) {
393 efree(remove->method.class);
394 }
395 if (remove->method.name) {
396 efree(remove->method.name);
397 }
398 break;
399
400 case NUMERIC_FUNCTION_PARAM:
401 case STR_PARAM:
402 case OP_PARAM:
403 case EVAL_PARAM:
404 case SHELL_PARAM:
405 case COND_PARAM:
406 case RUN_PARAM:
407 if (remove->str) {
408 efree(remove->str);
409 }
410 break;
411
412 case NUMERIC_FILE_PARAM:
413 case FILE_PARAM:
414 if (remove->file.name) {
415 efree(remove->file.name);
416 }
417 break;
418
419 default: {
420 /* nothing */
421 }
422 }
423
424 free(remove);
425 remove = NULL;
426
427 if (next)
428 remove = next;
429 else break;
430 }
431
432 stack->next = NULL;
433 }
434 } /* }}} */
435
436 /* {{{ */
phpdbg_stack_push(phpdbg_param_t * stack,phpdbg_param_t * param)437 PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) {
438 phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t));
439
440 if (!next) {
441 return;
442 }
443
444 *(next) = *(param);
445
446 next->next = NULL;
447
448 if (stack->top == NULL) {
449 stack->top = next;
450 next->top = NULL;
451 stack->next = next;
452 } else {
453 stack->top->next = next;
454 next->top = stack->top;
455 stack->top = next;
456 }
457
458 stack->len++;
459 } /* }}} */
460
461 /* {{{ */
phpdbg_stack_separate(phpdbg_param_t * param)462 PHPDBG_API void phpdbg_stack_separate(phpdbg_param_t *param) {
463 phpdbg_param_t *stack = calloc(1, sizeof(phpdbg_param_t));
464
465 stack->type = STACK_PARAM;
466 stack->next = param->next;
467 param->next = stack;
468 stack->top = param->top;
469 } /* }}} */
470
phpdbg_stack_verify(const phpdbg_command_t * command,phpdbg_param_t ** stack)471 PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack) {
472 if (command) {
473 char buffer[128] = {0,};
474 const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL;
475 const char *arg = command->args;
476 zend_ulong least = 0L,
477 received = 0L,
478 current = 0L;
479 bool optional = 0;
480
481 /* check for arg spec */
482 if (!(arg) || !(*arg)) {
483 if (!top || top->type == STACK_PARAM) {
484 return SUCCESS;
485 }
486
487 phpdbg_error("The command \"%s\" expected no arguments",
488 phpdbg_command_name(command, buffer));
489 return FAILURE;
490 }
491
492 least = 0L;
493
494 /* count least amount of arguments */
495 while (arg && *arg) {
496 if (arg[0] == '|') {
497 break;
498 }
499 least++;
500 arg++;
501 }
502
503 arg = command->args;
504
505 #define verify_arg(e, a, t) if (!(a)) { \
506 if (!optional) { \
507 phpdbg_error("The command \"%s\" expected %s and got nothing at parameter "ZEND_ULONG_FMT, \
508 phpdbg_command_name(command, buffer), \
509 (e), \
510 current); \
511 return FAILURE;\
512 } \
513 } else if ((a)->type != (t)) { \
514 phpdbg_error("The command \"%s\" expected %s and got %s at parameter "ZEND_ULONG_FMT, \
515 phpdbg_command_name(command, buffer), \
516 (e),\
517 phpdbg_get_param_type((a)), \
518 current); \
519 return FAILURE; \
520 }
521
522 while (arg && *arg) {
523 if (top && top->type == STACK_PARAM) {
524 break;
525 }
526
527 current++;
528
529 switch (*arg) {
530 case '|': {
531 current--;
532 optional = 1;
533 arg++;
534 } continue;
535
536 case 'i': verify_arg("raw input", top, STR_PARAM); break;
537 case 's': verify_arg("string", top, STR_PARAM); break;
538 case 'n': verify_arg("number", top, NUMERIC_PARAM); break;
539 case 'm': verify_arg("method", top, METHOD_PARAM); break;
540 case 'a': verify_arg("address", top, ADDR_PARAM); break;
541 case 'f': verify_arg("file:line", top, FILE_PARAM); break;
542 case 'c': verify_arg("condition", top, COND_PARAM); break;
543 case 'o': verify_arg("opcode", top, OP_PARAM); break;
544 case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break;
545
546 case '*': { /* do nothing */ } break;
547 }
548
549 if (top) {
550 top = top->next;
551 } else {
552 break;
553 }
554
555 received++;
556 arg++;
557 }
558
559 #undef verify_arg
560
561 if ((received < least)) {
562 phpdbg_error("The command \"%s\" expected at least "ZEND_ULONG_FMT" arguments (%s) and received "ZEND_ULONG_FMT,
563 phpdbg_command_name(command, buffer),
564 least,
565 command->args,
566 received);
567 return FAILURE;
568 }
569 }
570
571 return SUCCESS;
572 }
573
574 /* {{{ */
phpdbg_stack_resolve(const phpdbg_command_t * commands,const phpdbg_command_t * parent,phpdbg_param_t ** top)575 PHPDBG_API const phpdbg_command_t *phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top) {
576 const phpdbg_command_t *command = commands;
577 phpdbg_param_t *name = *top;
578 const phpdbg_command_t *matched[3] = {NULL, NULL, NULL};
579 zend_ulong matches = 0L;
580
581 while (command && command->name && command->handler) {
582 if (name->len == 1 || command->name_len >= name->len) {
583 /* match single letter alias */
584 if (command->alias && (name->len == 1)) {
585 if (command->alias == (*name->str)) {
586 matched[matches] = command;
587 matches++;
588 }
589 } else {
590 /* match full, case insensitive, command name */
591 if (strncasecmp(command->name, name->str, name->len) == SUCCESS) {
592 if (matches < 3) {
593 /* only allow abbreviating commands that can be aliased */
594 if ((name->len != command->name_len && command->alias) || name->len == command->name_len) {
595 matched[matches] = command;
596 matches++;
597 }
598
599 /* exact match */
600 if (name->len == command->name_len) {
601 break;
602 }
603 } else {
604 break;
605 }
606 }
607 }
608 }
609
610 command++;
611 }
612
613 switch (matches) {
614 case 0:
615 if (parent) {
616 phpdbg_error("The command \"%s %s\" could not be found", parent->name, name->str);
617 } else {
618 phpdbg_error("The command \"%s\" could not be found", name->str);
619 }
620 return parent;
621
622 case 1:
623 (*top) = (*top)->next;
624
625 command = matched[0];
626 break;
627
628 default: {
629 char *list = NULL;
630 uint32_t it = 0;
631 size_t pos = 0;
632
633 while (it < matches) {
634 if (!list) {
635 list = emalloc(matched[it]->name_len + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0));
636 } else {
637 list = erealloc(list, (pos + matched[it]->name_len) + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0));
638 }
639 memcpy(&list[pos], matched[it]->name, matched[it]->name_len);
640 pos += matched[it]->name_len;
641 if ((it + 1) < matches) {
642 memcpy(&list[pos], ", ", sizeof(", ") - 1);
643 pos += (sizeof(", ") - 1);
644 }
645
646 list[pos] = 0;
647 it++;
648 }
649
650 /* ", " separated matches */
651 phpdbg_error("The command \"%s\" is ambiguous, matching "ZEND_ULONG_FMT" commands (%s)", name->str, matches, list);
652 efree(list);
653
654 return NULL;
655 }
656 }
657
658 if (command->subs && (*top) && ((*top)->type == STR_PARAM)) {
659 return phpdbg_stack_resolve(command->subs, command, top);
660 } else {
661 return command;
662 }
663
664 return NULL;
665 } /* }}} */
666
phpdbg_internal_stack_execute(phpdbg_param_t * stack,bool allow_async_unsafe)667 static int phpdbg_internal_stack_execute(phpdbg_param_t *stack, bool allow_async_unsafe) {
668 const phpdbg_command_t *handler = NULL;
669 phpdbg_param_t *top = (phpdbg_param_t *) stack->next;
670
671 switch (top->type) {
672 case EVAL_PARAM:
673 phpdbg_activate_err_buf(0);
674 phpdbg_free_err_buf();
675 return PHPDBG_COMMAND_HANDLER(ev)(top);
676
677 case RUN_PARAM:
678 if (!allow_async_unsafe) {
679 phpdbg_error("run command is disallowed during hard interrupt");
680 }
681 phpdbg_activate_err_buf(0);
682 phpdbg_free_err_buf();
683 return PHPDBG_COMMAND_HANDLER(run)(top);
684
685 case SHELL_PARAM:
686 if (!allow_async_unsafe) {
687 phpdbg_error("sh command is disallowed during hard interrupt");
688 return FAILURE;
689 }
690 phpdbg_activate_err_buf(0);
691 phpdbg_free_err_buf();
692 return PHPDBG_COMMAND_HANDLER(sh)(top);
693
694 case STR_PARAM: {
695 handler = phpdbg_stack_resolve(phpdbg_prompt_commands, NULL, &top);
696
697 if (handler) {
698 if (!allow_async_unsafe && !(handler->flags & PHPDBG_ASYNC_SAFE)) {
699 phpdbg_error("%s command is disallowed during hard interrupt", handler->name);
700 return FAILURE;
701 }
702
703 if (phpdbg_stack_verify(handler, &top) == SUCCESS) {
704 phpdbg_activate_err_buf(0);
705 phpdbg_free_err_buf();
706 return handler->handler(top);
707 }
708 }
709 } return FAILURE;
710
711 default:
712 phpdbg_error("The first parameter makes no sense !");
713 return FAILURE;
714 }
715
716 return SUCCESS;
717 } /* }}} */
718
719 /* {{{ */
phpdbg_stack_execute(phpdbg_param_t * stack,bool allow_async_unsafe)720 PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, bool allow_async_unsafe) {
721 phpdbg_param_t *top = stack;
722
723 if (stack->type != STACK_PARAM) {
724 phpdbg_error("The passed argument was not a stack !");
725 return FAILURE;
726 }
727
728 if (!stack->len) {
729 phpdbg_error("The stack contains nothing !");
730 return FAILURE;
731 }
732
733 do {
734 if (top->type == STACK_PARAM) {
735 int result;
736 if ((result = phpdbg_internal_stack_execute(top, allow_async_unsafe)) != SUCCESS) {
737 return result;
738 }
739 }
740 } while ((top = top->next));
741
742 return SUCCESS;
743 } /* }}} */
744
phpdbg_read_input(const char * buffered)745 PHPDBG_API char *phpdbg_read_input(const char *buffered) /* {{{ */
746 {
747 char buf[PHPDBG_MAX_CMD];
748 char *buffer = NULL;
749
750 if ((PHPDBG_G(flags) & (PHPDBG_IS_STOPPING | PHPDBG_IS_RUNNING)) != PHPDBG_IS_STOPPING) {
751 if (buffered == NULL) {
752 #ifdef HAVE_PHPDBG_READLINE
753 # ifdef HAVE_UNISTD_H
754 /* EOF makes readline write prompt again in local console mode and
755 ignored if compiled without readline integration. */
756 if (!isatty(PHPDBG_G(io)[PHPDBG_STDIN].fd)) {
757 char buf[PHPDBG_MAX_CMD];
758 phpdbg_write("%s", phpdbg_get_prompt());
759 phpdbg_consume_stdin_line(buf);
760 buffer = estrdup(buf);
761 } else
762 # endif
763 {
764 char *cmd = readline(phpdbg_get_prompt());
765 PHPDBG_G(last_was_newline) = 1;
766
767 if (!cmd) {
768 PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
769 zend_bailout();
770 }
771
772 add_history(cmd);
773 buffer = estrdup(cmd);
774 free(cmd);
775 }
776 #else
777 phpdbg_write("%s", phpdbg_get_prompt());
778 phpdbg_consume_stdin_line(buf);
779 buffer = estrdup(buf);
780 #endif
781 } else {
782 buffer = estrdup(buffered);
783 }
784 }
785
786 if (buffer && isspace(*buffer)) {
787 char *trimmed = buffer;
788 while (isspace(*trimmed))
789 trimmed++;
790
791 trimmed = estrdup(trimmed);
792 efree(buffer);
793 buffer = trimmed;
794 }
795
796 if (buffer && strlen(buffer)) {
797 if (PHPDBG_G(buffer)) {
798 free(PHPDBG_G(buffer));
799 }
800 PHPDBG_G(buffer) = strdup(buffer);
801 } else if (PHPDBG_G(buffer)) {
802 if (buffer) {
803 efree(buffer);
804 }
805 buffer = estrdup(PHPDBG_G(buffer));
806 }
807
808 return buffer;
809 } /* }}} */
810
phpdbg_destroy_input(char ** input)811 PHPDBG_API void phpdbg_destroy_input(char **input) /*{{{ */
812 {
813 efree(*input);
814 } /* }}} */
815
phpdbg_ask_user_permission(const char * question)816 PHPDBG_API int phpdbg_ask_user_permission(const char *question) {
817 char buf[PHPDBG_MAX_CMD];
818 phpdbg_out("%s", question);
819 phpdbg_out(" (type y or n): ");
820
821 while (1) {
822 phpdbg_consume_stdin_line(buf);
823 if ((buf[1] == '\n' || (buf[1] == '\r' && buf[2] == '\n')) && (buf[0] == 'y' || buf[0] == 'n')) {
824 if (buf[0] == 'y') {
825 return SUCCESS;
826 }
827 return FAILURE;
828 }
829 phpdbg_out("Please enter either y (yes) or n (no): ");
830 }
831
832 return SUCCESS;
833 }
834