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