summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Weinand <bobwei9@hotmail.com>2014-04-21 23:29:25 +0200
committerBob Weinand <bobwei9@hotmail.com>2014-04-21 23:30:08 +0200
commit708af5fd8e3b279aa2f6343e369e26a67d8bedc1 (patch)
treec3a3dc604892553cb5ad67bc6abcc15a50b189cc
parent32d5f269a03b554a0d3ebc96fcacc842e3344e4e (diff)
parent447ec05f3160628ef6dc7f7eee34bae866af8e5c (diff)
downloadphp-git-708af5fd8e3b279aa2f6343e369e26a67d8bedc1.tar.gz
Merge sapi/phpdbg into PHP-5.6
-rw-r--r--sapi/phpdbg/.gitignore4
-rw-r--r--sapi/phpdbg/Makefile.frag10
-rw-r--r--sapi/phpdbg/config.m415
-rw-r--r--sapi/phpdbg/config.w329
-rw-r--r--sapi/phpdbg/phpdbg.c312
-rw-r--r--sapi/phpdbg/phpdbg.h76
-rw-r--r--sapi/phpdbg/phpdbg_bp.c102
-rw-r--r--sapi/phpdbg/phpdbg_bp.h2
-rw-r--r--sapi/phpdbg/phpdbg_break.c126
-rw-r--r--sapi/phpdbg/phpdbg_break.h23
-rw-r--r--sapi/phpdbg/phpdbg_btree.c221
-rw-r--r--sapi/phpdbg/phpdbg_btree.h65
-rw-r--r--sapi/phpdbg/phpdbg_cmd.c674
-rw-r--r--sapi/phpdbg/phpdbg_cmd.h112
-rw-r--r--sapi/phpdbg/phpdbg_frame.c4
-rw-r--r--sapi/phpdbg/phpdbg_help.c1393
-rw-r--r--sapi/phpdbg/phpdbg_help.h60
-rw-r--r--sapi/phpdbg/phpdbg_info.c16
-rw-r--r--sapi/phpdbg/phpdbg_info.h12
-rw-r--r--sapi/phpdbg/phpdbg_lexer.c2271
-rw-r--r--sapi/phpdbg/phpdbg_lexer.h348
-rw-r--r--sapi/phpdbg/phpdbg_lexer.l131
-rw-r--r--sapi/phpdbg/phpdbg_list.c187
-rw-r--r--sapi/phpdbg/phpdbg_list.h8
-rw-r--r--sapi/phpdbg/phpdbg_opcode.c5
-rw-r--r--sapi/phpdbg/phpdbg_parser.y168
-rw-r--r--sapi/phpdbg/phpdbg_print.c175
-rw-r--r--sapi/phpdbg/phpdbg_print.h13
-rw-r--r--sapi/phpdbg/phpdbg_prompt.c883
-rw-r--r--sapi/phpdbg/phpdbg_prompt.h13
-rw-r--r--sapi/phpdbg/phpdbg_set.c258
-rw-r--r--sapi/phpdbg/phpdbg_set.h15
-rw-r--r--sapi/phpdbg/phpdbg_utils.c61
-rw-r--r--sapi/phpdbg/phpdbg_utils.h27
-rw-r--r--sapi/phpdbg/phpdbg_watch.c789
-rw-r--r--sapi/phpdbg/phpdbg_watch.h112
-rw-r--r--sapi/phpdbg/phpdbg_win.c42
-rw-r--r--sapi/phpdbg/phpdbg_win.h37
-rw-r--r--sapi/phpdbg/test.php46
39 files changed, 6802 insertions, 2023 deletions
diff --git a/sapi/phpdbg/.gitignore b/sapi/phpdbg/.gitignore
index 297efcbc42..af445861ac 100644
--- a/sapi/phpdbg/.gitignore
+++ b/sapi/phpdbg/.gitignore
@@ -1,5 +1,7 @@
.libs/
-./phpdbg
+phpdbg
*.lo
*.o
build
+phpdbg_parser.c
+phpdbg_parser.h
diff --git a/sapi/phpdbg/Makefile.frag b/sapi/phpdbg/Makefile.frag
index 5be6d5b00f..45768de2ef 100644
--- a/sapi/phpdbg/Makefile.frag
+++ b/sapi/phpdbg/Makefile.frag
@@ -8,6 +8,15 @@ $(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_PHPDBG)
+$(builddir)/sapi/phpdbg/phpdbg_lexer.lo: $(srcdir)/sapi/phpdbg/phpdbg_parser.h
+
+$(srcdir)/sapi/phpdbg/phpdbg_lexer.c: $(srcdir)/sapi/phpdbg/phpdbg_lexer.l
+ @(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date -cbdFo sapi/phpdbg/phpdbg_lexer.c sapi/phpdbg/phpdbg_lexer.l)
+
+$(srcdir)/sapi/phpdbg/phpdbg_parser.h: $(srcdir)/sapi/phpdbg/phpdbg_parser.c
+$(srcdir)/sapi/phpdbg/phpdbg_parser.c: $(srcdir)/sapi/phpdbg/phpdbg_parser.y
+ @$(YACC) -p phpdbg_ -v -d $(srcdir)/sapi/phpdbg/phpdbg_parser.y -o $@
+
install-phpdbg: $(BUILD_BINARY)
@echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/"
@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
@@ -25,4 +34,3 @@ test-phpdbg:
.PHONY: clean-phpdbg test-phpdbg
-
diff --git a/sapi/phpdbg/config.m4 b/sapi/phpdbg/config.m4
index 274e6409d0..3534d90124 100644
--- a/sapi/phpdbg/config.m4
+++ b/sapi/phpdbg/config.m4
@@ -3,10 +3,10 @@ dnl $Id$
dnl
PHP_ARG_ENABLE(phpdbg, for phpdbg support,
-[ --enable-phpdbg Build phpdbg], yes, yes)
+[ --enable-phpdbg Build phpdbg], no, no)
PHP_ARG_ENABLE(phpdbg-debug, for phpdbg debug build,
-[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
+[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
if test "$PHP_PHPDBG" != "no"; then
AC_DEFINE(HAVE_PHPDBG, 1, [ ])
@@ -18,17 +18,22 @@ if test "$PHP_PHPDBG" != "no"; then
fi
PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
- PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c"
+ PHP_PHPDBG_FILES="phpdbg.c phpdbg_parser.c phpdbg_lexer.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_btree.c"
+ if test "$PHP_READLINE" != "no"; then
+ PHPDBG_EXTRA_LIBS="-lreadline"
+ fi
+
PHP_SUBST(PHP_PHPDBG_CFLAGS)
PHP_SUBST(PHP_PHPDBG_FILES)
-
+ PHP_SUBST(PHPDBG_EXTRA_LIBS)
+
PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag])
PHP_SELECT_SAPI(phpdbg, program, $PHP_PHPDBG_FILES, $PHP_PHPDBG_CFLAGS, [$(SAPI_PHPDBG_PATH)])
BUILD_BINARY="sapi/phpdbg/phpdbg"
BUILD_SHARED="sapi/phpdbg/libphpdbg.la"
-
+
BUILD_PHPDBG="\$(LIBTOOL) --mode=link \
\$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
\$(PHP_GLOBAL_OBJS) \
diff --git a/sapi/phpdbg/config.w32 b/sapi/phpdbg/config.w32
index 29031507b3..fcc2b61a87 100644
--- a/sapi/phpdbg/config.w32
+++ b/sapi/phpdbg/config.w32
@@ -1,19 +1,18 @@
-ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes');
+ARG_ENABLE('phpdbg', 'Build phpdbg', 'no');
ARG_ENABLE('phpdbgs', 'Build phpdbg shared', 'no');
-PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c';
+PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_win.c phpdbg_btree.c';
PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll';
PHPDBG_EXE='phpdbg.exe';
if (PHP_PHPDBG == "yes") {
- /* build phpdbg binary */
SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE);
ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib");
+ DEFINE("CFLAGS", configure_subst.item("CFLAGS") + " /EHa");
}
if (PHP_PHPDBGS == "yes") {
SAPI('phpdbgs', PHPDBG_SOURCES, PHPDBG_DLL, '/D PHP_PHPDBG_EXPORTS /I win32');
ADD_FLAG("LIBS_PHPDBGS", "ws2_32.lib user32.lib");
+ DEFINE("CFLAGS", configure_subst.item("CFLAGS") + " /EHa");
}
-
-
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c
index 51a328d0b5..064e266082 100644
--- a/sapi/phpdbg/phpdbg.c
+++ b/sapi/phpdbg/phpdbg.c
@@ -18,7 +18,7 @@
+----------------------------------------------------------------------+
*/
-#ifndef ZEND_SIGNALS
+#if !defined(ZEND_SIGNALS) || defined(_WIN32)
# include <signal.h>
#endif
#include "phpdbg.h"
@@ -28,6 +28,7 @@
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
+#include "zend_alloc.h"
/* {{{ remote console headers */
#ifndef _WIN32
@@ -61,16 +62,15 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
pg->exec = NULL;
pg->exec_len = 0;
+ pg->buffer = NULL;
pg->ops = NULL;
pg->vmret = 0;
pg->bp_count = 0;
- pg->lcmd = NULL;
pg->flags = PHPDBG_DEFAULT_FLAGS;
pg->oplog = NULL;
pg->io[PHPDBG_STDIN] = NULL;
pg->io[PHPDBG_STDOUT] = NULL;
pg->io[PHPDBG_STDERR] = NULL;
- memset(&pg->lparam, 0, sizeof(phpdbg_param_t));
pg->frame.num = 0;
} /* }}} */
@@ -145,6 +145,7 @@ static void php_phpdbg_destroy_registered(void *data) /* {{{ */
function TSRMLS_CC);
} /* }}} */
+
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
{
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
@@ -157,7 +158,9 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
-
+
+ phpdbg_setup_watchpoints(TSRMLS_C);
+
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
@@ -178,7 +181,14 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
zend_hash_destroy(&PHPDBG_G(seek));
zend_hash_destroy(&PHPDBG_G(registered));
+ zend_hash_destroy(&PHPDBG_G(watchpoints));
+ zend_llist_destroy(&PHPDBG_G(watchlist_mem));
+ if (PHPDBG_G(buffer)) {
+ efree(PHPDBG_G(buffer));
+ PHPDBG_G(buffer) = NULL;
+ }
+
if (PHPDBG_G(exec)) {
efree(PHPDBG_G(exec));
PHPDBG_G(exec) = NULL;
@@ -209,24 +219,24 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
return SUCCESS;
} /* }}} */
-/* {{{ proto mixed phpdbg_exec(string context)
+/* {{{ proto mixed phpdbg_exec(string context)
Attempt to set the execution context for phpdbg
If the execution context was set previously it is returned
- If the execution context was not set previously boolean true is returned
+ If the execution context was not set previously boolean true is returned
If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
-static PHP_FUNCTION(phpdbg_exec)
+static PHP_FUNCTION(phpdbg_exec)
{
char *exec = NULL;
- zend_ulong exec_len = 0L;
+ int exec_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) {
return;
}
-
+
{
struct stat sb;
zend_bool result = 1;
-
+
if (VCWD_STAT(exec, &sb) != FAILURE) {
if (sb.st_mode & (S_IFREG|S_IFLNK)) {
if (PHPDBG_G(exec)) {
@@ -234,11 +244,11 @@ static PHP_FUNCTION(phpdbg_exec)
efree(PHPDBG_G(exec));
result = 0;
}
-
+
PHPDBG_G(exec) = estrndup(exec, exec_len);
PHPDBG_G(exec_len) = exec_len;
-
- if (result)
+
+ if (result)
ZVAL_BOOL(return_value, 1);
} else {
zend_error(
@@ -259,9 +269,9 @@ static PHP_FUNCTION(phpdbg_exec)
static PHP_FUNCTION(phpdbg_break)
{
if (ZEND_NUM_ARGS() > 0) {
- long type;
+ long type = 0;
char *expr = NULL;
- zend_uint expr_len = 0;
+ int expr_len = 0;
phpdbg_param_t param;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) {
@@ -269,28 +279,7 @@ static PHP_FUNCTION(phpdbg_break)
}
phpdbg_parse_param(expr, expr_len, &param TSRMLS_CC);
-
- switch (type) {
- case METHOD_PARAM:
- phpdbg_do_break_method(&param, NULL TSRMLS_CC);
- break;
-
- case FILE_PARAM:
- phpdbg_do_break_file(&param, NULL TSRMLS_CC);
- break;
-
- case NUMERIC_PARAM:
- phpdbg_do_break_lineno(&param, NULL TSRMLS_CC);
- break;
-
- case STR_PARAM:
- phpdbg_do_break_func(&param, NULL TSRMLS_CC);
- break;
-
- default: zend_error(
- E_WARNING, "unrecognized parameter type %ld", type);
- }
-
+ phpdbg_do_break(&param TSRMLS_CC);
phpdbg_clear_param(&param TSRMLS_CC);
} else if (EG(current_execute_data) && EG(active_op_array)) {
@@ -319,9 +308,9 @@ static PHP_FUNCTION(phpdbg_clear)
/* {{{ proto void phpdbg_color(integer element, string color) */
static PHP_FUNCTION(phpdbg_color)
{
- long element;
- char *color;
- zend_uint color_len;
+ long element = 0L;
+ char *color = NULL;
+ int color_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) {
return;
@@ -341,8 +330,8 @@ static PHP_FUNCTION(phpdbg_color)
/* {{{ proto void phpdbg_prompt(string prompt) */
static PHP_FUNCTION(phpdbg_prompt)
{
- char *prompt;
- zend_uint prompt_len;
+ char *prompt = NULL;
+ int prompt_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) {
return;
@@ -403,9 +392,9 @@ static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /*
if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
return FAILURE;
}
-
+
phpdbg_booted=1;
-
+
return SUCCESS;
} /* }}} */
@@ -585,7 +574,6 @@ const opt_struct OPTIONS[] = { /* {{{ */
{'z', 1, "load zend_extension"},
/* phpdbg options */
{'q', 0, "no banner"},
- {'e', 1, "exec"},
{'v', 0, "disable quietness"},
{'s', 0, "enable stepping"},
{'b', 0, "boring colours"},
@@ -615,10 +603,10 @@ const char phpdbg_ini_hardcoded[] =
/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
#define INI_DEFAULT(name, value) \
- Z_SET_REFCOUNT(tmp, 0); \
- Z_UNSET_ISREF(tmp); \
- ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
- zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
+ Z_SET_REFCOUNT(tmp, 0); \
+ Z_UNSET_ISREF(tmp); \
+ ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
+ zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
{
@@ -666,11 +654,11 @@ static inline void phpdbg_sigint_handler(int signo) /* {{{ */
int phpdbg_open_socket(const char *interface, short port) /* {{{ */
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
-
+
switch (fd) {
case -1:
return -1;
-
+
default: {
int reuse = 1;
@@ -678,27 +666,27 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */
case -1:
close(fd);
return -2;
-
+
default: {
struct sockaddr_in address;
-
+
memset(&address, 0, sizeof(address));
-
+
address.sin_port = htons(port);
address.sin_family = AF_INET;
-
+
if ((*interface == '*')) {
- address.sin_addr.s_addr = htonl(INADDR_ANY);
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
} else if (!inet_pton(AF_INET, interface, &address.sin_addr)) {
close(fd);
return -3;
}
-
+
switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) {
case -1:
close(fd);
return -4;
-
+
default: {
listen(fd, 5);
}
@@ -707,28 +695,28 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */
}
}
}
-
+
return fd;
} /* }}} */
static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */
-{
+{
if ((*socket)[0] >= 0) {
shutdown(
(*socket)[0], SHUT_RDWR);
close((*socket)[0]);
}
-
+
if (streams[0]) {
fclose(streams[0]);
}
-
+
if ((*socket)[1] >= 0) {
shutdown(
(*socket)[1], SHUT_RDWR);
close((*socket)[1]);
}
-
+
if (streams[1]) {
fclose(streams[1]);
}
@@ -798,14 +786,34 @@ int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*sock
dup2((*socket)[0], fileno(stdin));
dup2((*socket)[1], fileno(stdout));
-
+
setbuf(stdout, NULL);
streams[0] = fdopen((*socket)[0], "r");
streams[1] = fdopen((*socket)[1], "w");
-
+
return SUCCESS;
} /* }}} */
+
+void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) {
+ int is_handled = FAILURE;
+ TSRMLS_FETCH();
+
+ switch (sig) {
+ case SIGBUS:
+ case SIGSEGV:
+ is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC);
+ if (is_handled == FAILURE) {
+#ifdef ZEND_SIGNALS
+ zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL TSRMLS_CC);
+#else
+ sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
+#endif
+ }
+ break;
+ }
+
+}
#endif
int main(int argc, char **argv) /* {{{ */
@@ -852,6 +860,10 @@ int main(int argc, char **argv) /* {{{ */
#endif
#ifndef _WIN32
+ struct sigaction signal_struct;
+ signal_struct.sa_sigaction = phpdbg_signal_handler;
+ signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
+
address = strdup("127.0.0.1");
socket[0] = -1;
socket[1] = -1;
@@ -888,17 +900,17 @@ phpdbg_main:
bp_tmp_file = NULL;
}
}
+
+ if (!bp_tmp_file) {
+ phpdbg_error("Unable to create temporary file");
+ return 1;
+ }
#else
if (!mkstemp(bp_tmp_file)) {
memset(bp_tmp_file, 0, sizeof(bp_tmp_file));
}
#endif
- if (!bp_tmp_file) {
- phpdbg_error(
- "Unable to create temporary file");
- return 1;
- }
}
ini_entries = NULL;
ini_entries_len = 0;
@@ -920,8 +932,7 @@ phpdbg_main:
run = 0;
step = 0;
sapi_name = NULL;
-
-
+
while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
switch (opt) {
case 'r':
@@ -965,7 +976,7 @@ phpdbg_main:
ini_entries_len += len + sizeof("=1\n\0") - 2;
}
} break;
-
+
case 'z':
zend_extensions_len++;
if (zend_extensions) {
@@ -976,16 +987,6 @@ phpdbg_main:
/* begin phpdbg options */
- case 'e': { /* set execution context */
- exec_len = strlen(php_optarg);
- if (exec_len) {
- if (exec) {
- free(exec);
- }
- exec = strdup(php_optarg);
- }
- } break;
-
case 'S': { /* set SAPI name */
if (sapi_name) {
free(sapi_name);
@@ -1001,7 +1002,7 @@ phpdbg_main:
if (init_file) {
free(init_file);
}
-
+
init_file_len = strlen(php_optarg);
if (init_file_len) {
init_file = strdup(php_optarg);
@@ -1038,7 +1039,7 @@ phpdbg_main:
#ifndef _WIN32
/* if you pass a listen port, we will accept input on listen port */
/* and write output to listen port * 2 */
-
+
case 'l': { /* set listen ports */
if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) {
if (sscanf(php_optarg, "%d", &listen[0]) != 1) {
@@ -1050,7 +1051,7 @@ phpdbg_main:
}
}
} break;
-
+
case 'a': { /* set bind address */
free(address);
if (!php_optarg) {
@@ -1076,6 +1077,19 @@ phpdbg_main:
} break;
}
}
+
+ /* set exec if present on command line */
+ if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS))
+ {
+ exec_len = strlen(argv[php_optind]);
+ if (exec_len) {
+ if (exec) {
+ free(exec);
+ }
+ exec = strdup(argv[php_optind]);
+ }
+ php_optind++;
+ }
#ifndef _WIN32
/* setup remote server if necessary */
@@ -1093,7 +1107,7 @@ phpdbg_main:
if (sapi_name) {
phpdbg->name = sapi_name;
}
-
+
phpdbg->ini_defaults = phpdbg_ini_defaults;
phpdbg->phpinfo_as_text = 1;
phpdbg->php_ini_ignore_cwd = 1;
@@ -1114,14 +1128,14 @@ phpdbg_main:
memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
}
ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
-
+
if (zend_extensions_len) {
zend_ulong zend_extension = 0L;
-
+
while (zend_extension < zend_extensions_len) {
const char *ze = zend_extensions[zend_extension];
size_t ze_len = strlen(ze);
-
+
ini_entries = realloc(
ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
@@ -1133,40 +1147,81 @@ phpdbg_main:
free(zend_extensions[zend_extension]);
zend_extension++;
}
-
+
free(zend_extensions);
}
phpdbg->ini_entries = ini_entries;
-
+
if (phpdbg->startup(phpdbg) == SUCCESS) {
- php_request_startup(TSRMLS_C);
+#ifdef _WIN32
+ EXCEPTION_POINTERS *xp;
+ __try {
+#endif
+ zend_mm_heap *mm_heap = zend_mm_set_heap(NULL TSRMLS_CC);
+#if ZEND_DEBUG
+ if (!mm_heap->use_zend_alloc) {
+ mm_heap->_malloc = malloc;
+ mm_heap->_realloc = realloc;
+ mm_heap->_free = free;
+#endif
+ PHPDBG_G(original_free_function) = mm_heap->_free;
+ mm_heap->_free = phpdbg_watch_efree;
+ mm_heap->use_zend_alloc = 0;
+#if ZEND_DEBUG
+ }
+#endif
+ zend_mm_set_heap(mm_heap TSRMLS_CC);
+
+ zend_activate(TSRMLS_C);
+
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
+ zend_try {
+ zend_signal_activate(TSRMLS_C);
+ } zend_end_try();
+#endif
+
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
+ zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
+ zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
+#elif !defined(_WIN32)
+ sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
+ sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
+#endif
+
+ if (php_request_startup(TSRMLS_C) == SUCCESS) {
+ int i;
+
+ SG(request_info).argc = argc - php_optind + 1;
+ SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
+ for (i = SG(request_info).argc; --i;) {
+ SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
+ }
+ SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup("");
+
+ php_hash_environment(TSRMLS_C);
+ }
+
+ /* make sure to turn off buffer for ev command */
+ php_output_activate(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
/* do not install sigint handlers for remote consoles */
/* sending SIGINT then provides a decent way of shutting down the server */
-#ifdef ZEND_SIGNALS
-# ifndef _WIN32
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
if (listen[0] < 0) {
-# endif
- zend_try {
- zend_signal_activate(TSRMLS_C);
- zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
- } zend_end_try();
-# ifndef _WIN32
+ zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try();
}
-# endif
-#else
-# ifndef _WIN32
+#elif !defined(_WIN32)
if (listen[0] < 0) {
-# endif
+#endif
signal(SIGINT, phpdbg_sigint_handler);
#ifndef _WIN32
}
#endif
-#endif
PG(modules_activated) = 0;
-
+
/* set flags from command line */
PHPDBG_G(flags) = flags;
@@ -1182,10 +1237,9 @@ phpdbg_main:
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
-
+
if (exec) { /* set execution context */
- PHPDBG_G(exec) = phpdbg_resolve_path(
- exec TSRMLS_CC);
+ PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC);
PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec));
free(exec);
@@ -1213,6 +1267,11 @@ phpdbg_main:
phpdbg_welcome((cleaning > 0) TSRMLS_CC);
}
+ /* auto compile */
+ if (PHPDBG_G(exec)) {
+ phpdbg_compile(TSRMLS_C);
+ }
+
/* initialize from file */
PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
zend_try {
@@ -1233,14 +1292,17 @@ phpdbg_main:
if (run) {
/* no need to try{}, run does it ... */
- PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC);
+ PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC);
if (run > 1) {
/* if -r is on the command line more than once just quit */
goto phpdbg_out;
}
}
+/* #ifndef for making compiler shutting up */
+#ifndef _WIN32
phpdbg_interact:
+#endif
/* phpdbg main() */
do {
zend_try {
@@ -1282,21 +1344,42 @@ phpdbg_interact:
} zend_end_try();
} while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
-phpdbg_out:
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+
+ /* this is just helpful */
+ PG(report_memleaks) = 0;
+
#ifndef _WIN32
+phpdbg_out:
if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
goto phpdbg_interact;
}
#endif
+#ifdef _WIN32
+ } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
+ phpdbg_error("Access violation (Segementation fault) encountered\ntrying to abort cleanly...");
+ }
+phpdbg_out:
+#endif
+
+ {
+ int i;
+ /* free argv */
+ for (i = SG(request_info).argc; --i;) {
+ efree(SG(request_info).argv[i]);
+ }
+ efree(SG(request_info).argv);
+ }
+
#ifndef ZTS
/* force cleanup of auto and core globals */
zend_hash_clean(CG(auto_globals));
memset(
&core_globals, 0, sizeof(php_core_globals));
#endif
-
if (ini_entries) {
free(ini_entries);
}
@@ -1318,6 +1401,7 @@ phpdbg_out:
} zend_end_try();
sapi_shutdown();
+
}
if (cleaning || remote) {
@@ -1331,7 +1415,7 @@ phpdbg_out:
#ifndef _WIN32
if (address) {
- free(address);
+ free(address);
}
#endif
diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h
index 68c505ca63..be009e40d0 100644
--- a/sapi/phpdbg/phpdbg.h
+++ b/sapi/phpdbg/phpdbg.h
@@ -39,6 +39,9 @@
#include "zend_globals.h"
#include "zend_ini_scanner.h"
#include "zend_stream.h"
+#ifndef _WIN32
+# include "zend_signal.h"
+#endif
#include "SAPI.h"
#include <fcntl.h>
#include <sys/types.h>
@@ -68,6 +71,8 @@
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
+#include "phpdbg_btree.h"
+#include "phpdbg_watch.h"
#ifdef ZTS
# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
@@ -116,23 +121,26 @@
#define PHPDBG_IN_EVAL (1<<11)
#define PHPDBG_IS_STEPPING (1<<12)
-#define PHPDBG_IS_QUIET (1<<13)
-#define PHPDBG_IS_QUITTING (1<<14)
-#define PHPDBG_IS_COLOURED (1<<15)
-#define PHPDBG_IS_CLEANING (1<<16)
-
-#define PHPDBG_IN_UNTIL (1<<17)
-#define PHPDBG_IN_FINISH (1<<18)
-#define PHPDBG_IN_LEAVE (1<<19)
-
-#define PHPDBG_IS_REGISTERED (1<<20)
-#define PHPDBG_IS_STEPONEVAL (1<<21)
-#define PHPDBG_IS_INITIALIZING (1<<22)
-#define PHPDBG_IS_SIGNALED (1<<23)
-#define PHPDBG_IS_INTERACTIVE (1<<24)
-#define PHPDBG_IS_BP_ENABLED (1<<25)
-#define PHPDBG_IS_REMOTE (1<<26)
-#define PHPDBG_IS_DISCONNECTED (1<<27)
+#define PHPDBG_STEP_OPCODE (1<<13)
+#define PHPDBG_IS_QUIET (1<<14)
+#define PHPDBG_IS_QUITTING (1<<15)
+#define PHPDBG_IS_COLOURED (1<<16)
+#define PHPDBG_IS_CLEANING (1<<17)
+
+#define PHPDBG_IN_UNTIL (1<<18)
+#define PHPDBG_IN_FINISH (1<<19)
+#define PHPDBG_IN_LEAVE (1<<20)
+
+#define PHPDBG_IS_REGISTERED (1<<21)
+#define PHPDBG_IS_STEPONEVAL (1<<22)
+#define PHPDBG_IS_INITIALIZING (1<<23)
+#define PHPDBG_IS_SIGNALED (1<<24)
+#define PHPDBG_IS_INTERACTIVE (1<<25)
+#define PHPDBG_IS_BP_ENABLED (1<<26)
+#define PHPDBG_IS_REMOTE (1<<27)
+#define PHPDBG_IS_DISCONNECTED (1<<28)
+
+#define PHPDBG_SHOW_REFCOUNTS (1<<29)
#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE)
#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
@@ -149,7 +157,7 @@
#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */
#define PHPDBG_URL "http://phpdbg.com"
#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
-#define PHPDBG_VERSION "0.3.2"
+#define PHPDBG_VERSION "0.4.0"
#define PHPDBG_INIT_FILENAME ".phpdbginit"
/* }}} */
@@ -159,12 +167,25 @@
#define PHPDBG_STDERR 2
#define PHPDBG_IO_FDS 3 /* }}} */
+
/* {{{ structs */
ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */
HashTable registered; /* registered */
HashTable seek; /* seek oplines */
phpdbg_frame_t frame; /* frame */
+ zend_uint last_line; /* last executed line */
+
+#ifndef _WIN32
+ struct sigaction old_sigsegv_signal; /* segv signal handler */
+#endif
+
+ phpdbg_btree watchpoint_tree; /* tree with watchpoints */
+ phpdbg_btree watch_HashTables; /* tree with original dtors of watchpoints */
+ HashTable watchpoints; /* watchpoints */
+ zend_llist watchlist_mem; /* triggered watchpoints */
+ zend_bool watchpoint_hit; /* a watchpoint was hit */
+ void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */
char *exec; /* file to execute */
size_t exec_len; /* size of exec */
@@ -178,11 +199,24 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
char *prompt[2]; /* prompt */
const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */
-
- phpdbg_command_t *lcmd; /* last command */
- phpdbg_param_t lparam; /* last param */
+ char *buffer; /* buffer */
zend_ulong flags; /* phpdbg flags */
ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
+/* the beginning (= the important part) of the _zend_mm_heap struct defined in Zend/zend_alloc.c
+ Needed for realizing watchpoints */
+struct _zend_mm_heap {
+ int use_zend_alloc;
+ void *(*_malloc)(size_t);
+ void (*_free)(void *);
+ void *(*_realloc)(void *, size_t);
+ size_t free_bitmap;
+ size_t large_free_bitmap;
+ size_t block_size;
+ size_t compact_size;
+ zend_mm_segment *segments_list;
+ zend_mm_storage *storage;
+};
+
#endif /* PHPDBG_H */
diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c
index 511d1db57d..a18316a228 100644
--- a/sapi/phpdbg/phpdbg_bp.c
+++ b/sapi/phpdbg/phpdbg_bp.c
@@ -135,27 +135,27 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
switch (brake->type) {
case PHPDBG_BREAK_FILE: {
fprintf(handle,
- "break file %s:%lu\n",
+ "break %s:%lu\n",
((phpdbg_breakfile_t*)brake)->filename,
((phpdbg_breakfile_t*)brake)->line);
} break;
case PHPDBG_BREAK_SYM: {
fprintf(handle,
- "break func %s\n",
+ "break %s\n",
((phpdbg_breaksymbol_t*)brake)->symbol);
} break;
case PHPDBG_BREAK_METHOD: {
fprintf(handle,
- "break method %s::%s\n",
+ "break %s::%s\n",
((phpdbg_breakmethod_t*)brake)->class_name,
((phpdbg_breakmethod_t*)brake)->func_name);
} break;
case PHPDBG_BREAK_METHOD_OPLINE: {
fprintf(handle,
- "break address %s::%s#%ld\n",
+ "break %s::%s#%ld\n",
((phpdbg_breakopline_t*)brake)->class_name,
((phpdbg_breakopline_t*)brake)->func_name,
((phpdbg_breakopline_t*)brake)->opline_num);
@@ -163,21 +163,21 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
case PHPDBG_BREAK_FUNCTION_OPLINE: {
fprintf(handle,
- "break address %s#%ld\n",
+ "break %s#%ld\n",
((phpdbg_breakopline_t*)brake)->func_name,
((phpdbg_breakopline_t*)brake)->opline_num);
} break;
case PHPDBG_BREAK_FILE_OPLINE: {
fprintf(handle,
- "break address %s:%ld\n",
+ "break %s:#%ld\n",
((phpdbg_breakopline_t*)brake)->class_name,
((phpdbg_breakopline_t*)brake)->opline_num);
} break;
case PHPDBG_BREAK_OPCODE: {
fprintf(handle,
- "break op %s\n",
+ "break %s\n",
((phpdbg_breakop_t*)brake)->name);
} break;
@@ -209,7 +209,7 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
}
} else {
fprintf(
- handle, "break on %s\n", conditional->code);
+ handle, "break if %s\n", conditional->code);
}
} break;
}
@@ -221,14 +221,20 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */
{
- struct stat sb;
-
- if (VCWD_STAT(path, &sb) != FAILURE) {
- if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ php_stream_statbuf ssb;
+ char realpath[MAXPATHLEN];
+
+ if (php_stream_stat_path(path, &ssb) != FAILURE) {
+ if (ssb.sb.st_mode & (S_IFREG|S_IFLNK)) {
HashTable *broken;
phpdbg_breakfile_t new_break;
- size_t path_len = strlen(path);
-
+ size_t path_len = 0L;
+
+ if (VCWD_REALPATH(path, realpath)) {
+ path = realpath;
+ }
+ path_len = strlen(path);
+
if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],
path, path_len, (void**)&broken) == FAILURE) {
HashTable breaks;
@@ -324,9 +330,9 @@ PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char
PHPDBG_BREAK_MAPPING(new_break.id, class_table);
} else {
phpdbg_notice("Breakpoint exists at %s::%s", class_name, func_name);
- }
+ }
- efree(lcname);
+ efree(lcname);
} /* }}} */
PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */
@@ -355,7 +361,7 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{
PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC) /* {{{ */
{
phpdbg_breakline_t opline_break;
- if (op_array->last < brake->opline_num) {
+ if (op_array->last <= brake->opline_num) {
if (brake->class_name == NULL) {
phpdbg_error("There are only %d oplines in function %s (breaking at opline %ld impossible)", op_array->last, brake->func_name, brake->opline_num);
} else if (brake->func_name == NULL) {
@@ -760,56 +766,26 @@ PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_l
}
} /* }}} */
-PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
- if (input->argc > 3 && phpdbg_argv_is(2, "if")) {
- phpdbg_breakcond_t new_break;
- phpdbg_param_t new_param;
-
- zend_ulong expr_hash = 0L;
- size_t expr_len;
- const char *join = strstr(input->string, "if");
- const char *expr = (join) + sizeof("if");
-
- expr_len = strlen(expr);
- expr = phpdbg_trim(expr, expr_len, &expr_len);
- expr_hash = zend_inline_hash_func(expr, expr_len);
-
- {
- /* get a clean parameter from input string */
- size_t sparam_len = 0L;
- char *sparam = input->string;
-
- sparam[
- strstr(input->string, " ") - input->string] = 0;
- sparam_len = strlen(sparam);
-
- switch (phpdbg_parse_param(sparam, sparam_len, &new_param TSRMLS_CC)) {
- case EMPTY_PARAM:
- case NUMERIC_PARAM:
- phpdbg_clear_param(
- &new_param TSRMLS_CC);
- goto usage;
-
- default: { /* do nothing */ } break;
- }
-
- expr_hash += phpdbg_hash_param(&new_param TSRMLS_CC);
- }
-
- if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+ phpdbg_breakcond_t new_break;
+ phpdbg_param_t *condition;
+ zend_ulong hash = 0L;
+
+ if (param->next) {
+ condition = param->next;
+ hash = zend_inline_hash_func(condition->str, condition->len);
+
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) {
phpdbg_create_conditional_break(
- &new_break, &new_param, expr, expr_len, expr_hash TSRMLS_CC);
+ &new_break, param,
+ condition->str, condition->len, hash TSRMLS_CC);
} else {
phpdbg_notice(
- "Conditional break %s exists at the specified location", expr);
- }
-
- phpdbg_clear_param(&new_param TSRMLS_CC);
- } else {
-usage:
- phpdbg_error("usage: break at <func|method|file:line|address> if <expression>");
+ "Conditional break %s exists at the specified location", condition->str);
+ }
}
+
} /* }}} */
static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */
@@ -992,7 +968,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execut
for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position);
zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS;
- zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
zval *retval = NULL;
int orig_interactive = CG(interactive);
zval **orig_retval = EG(return_value_ptr_ptr);
diff --git a/sapi/phpdbg/phpdbg_bp.h b/sapi/phpdbg/phpdbg_bp.h
index b1a9ddf474..97980e7ed7 100644
--- a/sapi/phpdbg/phpdbg_bp.h
+++ b/sapi/phpdbg/phpdbg_bp.h
@@ -119,7 +119,7 @@ PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const cha
PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC);
-PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC); /* }}} */
/* {{{ Breakpoint Detection API */
PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
diff --git a/sapi/phpdbg/phpdbg_break.c b/sapi/phpdbg/phpdbg_break.c
index f56f76facd..be76b22b05 100644
--- a/sapi/phpdbg/phpdbg_break.c
+++ b/sapi/phpdbg/phpdbg_break.c
@@ -24,132 +24,32 @@
#include "phpdbg_opcode.h"
#include "phpdbg_break.h"
#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-PHPDBG_BREAK(file) /* {{{ */
-{
- switch (param->type) {
- case FILE_PARAM:
- phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(method) /* {{{ */
-{
- switch (param->type) {
- case METHOD_PARAM:
- phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(address) /* {{{ */
-{
- switch (param->type) {
- case ADDR_PARAM:
- phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
- break;
-
- case NUMERIC_METHOD_PARAM:
- phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC);
- break;
-
- case NUMERIC_FUNCTION_PARAM:
- phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC);
- break;
+#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10])
- case FILE_PARAM:
- phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(on) /* {{{ */
-{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
+/**
+ * Commands
+ */
+const phpdbg_command_t phpdbg_break_commands[] = {
+ PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c"),
+ PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n"),
+ PHPDBG_END_COMMAND
+};
PHPDBG_BREAK(at) /* {{{ */
{
- phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(lineno) /* {{{ */
-{
- switch (param->type) {
- case NUMERIC_PARAM: {
- if (PHPDBG_G(exec)) {
- phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
- } else {
- phpdbg_error("Execution context not set!");
- }
- } break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(func) /* {{{ */
-{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(op) /* {{{ */
-{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
+ phpdbg_set_breakpoint_at(param TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(del) /* {{{ */
{
- switch (param->type) {
- case NUMERIC_PARAM: {
- phpdbg_delete_breakpoint(param->num TSRMLS_CC);
- } break;
-
- phpdbg_default_switch_case();
- }
+ phpdbg_delete_breakpoint(param->num TSRMLS_CC);
return SUCCESS;
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_break.h b/sapi/phpdbg/phpdbg_break.h
index f90e351d6d..dc06da62b7 100644
--- a/sapi/phpdbg/phpdbg_break.h
+++ b/sapi/phpdbg/phpdbg_break.h
@@ -29,30 +29,9 @@
/**
* Printer Forward Declarations
*/
-PHPDBG_BREAK(file);
-PHPDBG_BREAK(func);
-PHPDBG_BREAK(method);
-PHPDBG_BREAK(address);
PHPDBG_BREAK(at);
-PHPDBG_BREAK(op);
-PHPDBG_BREAK(on);
-PHPDBG_BREAK(lineno);
PHPDBG_BREAK(del);
-/**
- * Commands
- */
-static const phpdbg_command_t phpdbg_break_commands[] = {
- PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1),
- PHPDBG_COMMAND_D_EX(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1),
- PHPDBG_COMMAND_D_EX(method, "specify breakpoint by class::method", 'm', break_method, NULL, 1),
- PHPDBG_COMMAND_D_EX(address, "specify breakpoint by address", 'a', break_address, NULL, 1),
- PHPDBG_COMMAND_D_EX(op, "specify breakpoint by opcode", 'O', break_op, NULL, 1),
- PHPDBG_COMMAND_D_EX(on, "specify breakpoint by condition", 'o', break_on, NULL, 1),
- PHPDBG_COMMAND_D_EX(at, "specify breakpoint by location and condition", 'A', break_at, NULL, 1),
- PHPDBG_COMMAND_D_EX(lineno, "specify breakpoint by line of currently executing file", 'l', break_lineno, NULL, 1),
- PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_break_commands[];
#endif /* PHPDBG_BREAK_H */
diff --git a/sapi/phpdbg/phpdbg_btree.c b/sapi/phpdbg/phpdbg_btree.c
new file mode 100644
index 0000000000..8fc2561e04
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_btree.c
@@ -0,0 +1,221 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg_btree.h"
+#include "phpdbg.h"
+
+#define CHOOSE_BRANCH(n) \
+ branch = branch->branches[!!(n)];
+
+#ifdef _Win32
+# define emalloc malloc
+# define efree free
+#endif
+
+/* depth in bits */
+void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) {
+ tree->depth = depth;
+ tree->branch = NULL;
+ tree->count = 0;
+}
+
+phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx) {
+ phpdbg_btree_branch *branch = tree->branch;
+ int i = tree->depth - 1;
+
+ if (branch == NULL) {
+ return NULL;
+ }
+
+ do {
+ if ((idx >> i) % 2 == 1) {
+ if (branch->branches[1]) {
+ CHOOSE_BRANCH(1);
+ } else {
+ return NULL;
+ }
+ } else {
+ if (branch->branches[0]) {
+ CHOOSE_BRANCH(0);
+ } else {
+ return NULL;
+ }
+ }
+ } while (i--);
+
+ return &branch->result;
+}
+
+phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx) {
+ phpdbg_btree_branch *branch = tree->branch;
+ int i = tree->depth - 1, last_superior_i = -1;
+ zend_bool had_alternative_branch = 0;
+
+ if (branch == NULL) {
+ return NULL;
+ }
+
+ /* find nearest watchpoint */
+ do {
+ /* an impossible branch was found if: */
+ if (!had_alternative_branch && (idx >> i) % 2 == 0 && !branch->branches[0]) {
+ /* there's no lower branch than idx */
+ if (last_superior_i == -1) {
+ /* failure */
+ return NULL;
+ }
+ /* reset state */
+ branch = tree->branch;
+ i = tree->depth - 1;
+ /* follow branch according to bits in idx until the last lower branch before the impossible branch */
+ do {
+ CHOOSE_BRANCH((idx >> i) % 2 == 1 && branch->branches[1]);
+ } while (--i > last_superior_i);
+ /* use now the lower branch of which we can be sure that it contains only branches lower than idx */
+ CHOOSE_BRANCH(0);
+ /* and choose the highest possible branch in the branch containing only branches lower than idx */
+ while (i--) {
+ CHOOSE_BRANCH(branch->branches[1]);
+ }
+ break;
+ }
+ /* follow branch according to bits in idx until having found an impossible branch */
+ if (had_alternative_branch || (idx >> i) % 2 == 1) {
+ if (branch->branches[1]) {
+ if (branch->branches[0]) {
+ last_superior_i = i;
+ }
+ CHOOSE_BRANCH(1);
+ } else {
+ CHOOSE_BRANCH(0);
+ had_alternative_branch = 1;
+ }
+ } else {
+ CHOOSE_BRANCH(0);
+ }
+ } while (i--);
+
+ return &branch->result;
+}
+
+phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx) {
+ phpdbg_btree_position pos;
+
+ pos.tree = tree;
+ pos.end = lower_idx;
+ pos.cur = higher_idx;
+
+ return pos;
+}
+
+phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos) {
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(pos->tree, pos->cur);
+
+ if (result == NULL || result->idx < pos->end) {
+ return NULL;
+ }
+
+ pos->cur = result->idx - 1;
+
+ return result;
+}
+
+int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags) {
+ int i = tree->depth - 1;
+ phpdbg_btree_branch **branch = &tree->branch;
+
+ do {
+ if (*branch == NULL) {
+ break;
+ }
+ branch = &(*branch)->branches[(idx >> i) % 2];
+ } while (i--);
+
+ if (*branch == NULL) {
+ if (!(flags & PHPDBG_BTREE_INSERT)) {
+ return FAILURE;
+ }
+
+ {
+ phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch));
+ do {
+ (*branch)->branches[!((idx >> i) % 2)] = NULL;
+ branch = &(*branch)->branches[(idx >> i) % 2];
+ *branch = ++memory;
+ } while (i--);
+ tree->count++;
+ }
+ } else if (!(flags & PHPDBG_BTREE_UPDATE)) {
+ return FAILURE;
+ }
+
+ (*branch)->result.idx = idx;
+ (*branch)->result.ptr = ptr;
+
+ return SUCCESS;
+}
+
+int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) {
+ int i = tree->depth;
+ phpdbg_btree_branch *branch = tree->branch;
+ int i_last_dual_branch = -1, last_dual_branch_branch;
+ phpdbg_btree_branch *last_dual_branch = NULL;
+
+ goto check_branch_existence;
+ do {
+ if (branch->branches[0] && branch->branches[1]) {
+ last_dual_branch = branch;
+ i_last_dual_branch = i;
+ last_dual_branch_branch = (idx >> i) % 2;
+ }
+ branch = branch->branches[(idx >> i) % 2];
+
+check_branch_existence:
+ if (branch == NULL) {
+ return FAILURE;
+ }
+ } while (i--);
+
+ tree->count--;
+
+ if (i_last_dual_branch == -1) {
+ efree(tree->branch);
+ tree->branch = NULL;
+ } else {
+ if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) {
+ phpdbg_btree_branch *original_branch = last_dual_branch->branches[!last_dual_branch_branch];
+
+ memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], (i_last_dual_branch + 1) * sizeof(phpdbg_btree_branch));
+ efree(last_dual_branch->branches[!last_dual_branch_branch]);
+ last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1;
+
+ branch = last_dual_branch->branches[!last_dual_branch_branch];
+ for (i = i_last_dual_branch; i--;) {
+ branch = (branch->branches[branch->branches[1] == ++original_branch] = last_dual_branch + i_last_dual_branch - i + 1);
+ }
+ } else {
+ efree(last_dual_branch->branches[last_dual_branch_branch]);
+ }
+
+ last_dual_branch->branches[last_dual_branch_branch] = NULL;
+ }
+
+ return SUCCESS;
+}
diff --git a/sapi/phpdbg/phpdbg_btree.h b/sapi/phpdbg/phpdbg_btree.h
new file mode 100644
index 0000000000..5fb217db35
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_btree.h
@@ -0,0 +1,65 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BTREE_H
+#define PHPDBG_BTREE_H
+
+#include "zend.h"
+
+typedef struct {
+ zend_ulong idx;
+ void *ptr;
+} phpdbg_btree_result;
+
+typedef union _phpdbg_btree_branch phpdbg_btree_branch;
+union _phpdbg_btree_branch {
+ phpdbg_btree_branch *branches[2];
+ phpdbg_btree_result result;
+};
+
+typedef struct {
+ zend_ulong count;
+ zend_ulong depth;
+ phpdbg_btree_branch *branch;
+} phpdbg_btree;
+
+typedef struct {
+ phpdbg_btree *tree;
+ zend_ulong cur;
+ zend_ulong end;
+} phpdbg_btree_position;
+
+void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth);
+phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx);
+phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx);
+phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx);
+phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos);
+int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx);
+
+#define PHPDBG_BTREE_INSERT 1
+#define PHPDBG_BTREE_UPDATE 2
+#define PHPDBG_BTREE_OVERWRITE (PHPDBG_BTREE_INSERT | PHPDBG_BTREE_UPDATE)
+
+int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags);
+#define phpdbg_btree_insert(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_INSERT)
+#define phpdbg_btree_update(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_UPDATE)
+#define phpdbg_btree_overwrite(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_OWERWRITE)
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_cmd.c b/sapi/phpdbg/phpdbg_cmd.c
index 1d78c53321..d4ce8ebc55 100644
--- a/sapi/phpdbg/phpdbg_cmd.c
+++ b/sapi/phpdbg/phpdbg_cmd.c
@@ -22,12 +22,32 @@
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+static inline const char *phpdbg_command_name(const phpdbg_command_t *command, char *buffer) {
+ size_t pos = 0;
+
+ if (command->parent) {
+ memcpy(&buffer[pos], command->parent->name, command->parent->name_len);
+ pos += command->parent->name_len;
+ memcpy(&buffer[pos], " ", sizeof(" ")-1);
+ pos += (sizeof(" ")-1);
+ }
+
+ memcpy(&buffer[pos], command->name, command->name_len);
+ pos += command->name_len;
+ buffer[pos] = 0;
+
+ return buffer;
+}
+
PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
switch (param->type) {
+ case STACK_PARAM:
+ return "stack";
case EMPTY_PARAM:
return "empty";
case ADDR_PARAM:
@@ -208,10 +228,19 @@ PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **point
PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */
{
switch ((dest->type = src->type)) {
+ case STACK_PARAM:
+ /* nope */
+ break;
+
case STR_PARAM:
dest->str = estrndup(src->str, src->len);
dest->len = src->len;
break;
+
+ case OP_PARAM:
+ dest->str = estrndup(src->str, src->len);
+ dest->len = src->len;
+ break;
case ADDR_PARAM:
dest->addr = src->addr;
@@ -226,6 +255,7 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des
dest->method.name = estrdup(src->method.name);
break;
+ case NUMERIC_FILE_PARAM:
case FILE_PARAM:
dest->file.name = estrdup(src->file.name);
dest->file.line = src->file.line;
@@ -246,6 +276,10 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des
break;
case EMPTY_PARAM: { /* do nothing */ } break;
+
+ default: {
+ /* not yet */
+ }
}
} /* }}} */
@@ -254,6 +288,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /
zend_ulong hash = param->type;
switch (param->type) {
+ case STACK_PARAM:
+ /* nope */
+ break;
+
case STR_PARAM:
hash += zend_inline_hash_func(param->str, param->len);
break;
@@ -291,6 +329,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /
break;
case EMPTY_PARAM: { /* do nothing */ } break;
+
+ default: {
+ /* not yet */
+ }
}
return hash;
@@ -301,7 +343,11 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa
if (l && r) {
if (l->type == r->type) {
switch (l->type) {
-
+ case STACK_PARAM:
+ /* nope, or yep */
+ return 1;
+ break;
+
case NUMERIC_FUNCTION_PARAM:
if (l->num != r->num) {
break;
@@ -356,112 +402,400 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa
case EMPTY_PARAM:
return 1;
+
+ default: {
+ /* not yet */
+ }
}
}
}
return 0;
} /* }}} */
-PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
-{
- char *p;
- char b[PHPDBG_MAX_CMD];
- int l=0;
- enum states {
- IN_BETWEEN,
- IN_WORD,
- IN_STRING
- } state = IN_BETWEEN;
- phpdbg_input_t **argv = NULL;
-
- argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*));
- (*argc) = 0;
-
-#define RESET_STATE() do { \
- phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \
- if (arg) { \
- b[l]=0; \
- arg->length = l; \
- arg->string = estrndup(b, arg->length); \
- arg->argv = NULL; \
- arg->argc = 0; \
- argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \
- argv[(*argc)++] = arg; \
- l = 0; \
- } \
- state = IN_BETWEEN; \
-} while (0)
-
- for (p = buffer; *p != '\0'; p++) {
- int c = (unsigned char) *p;
- switch (state) {
- case IN_BETWEEN:
- if (isspace(c)) {
- continue;
- }
- if (c == '"') {
- state = IN_STRING;
- continue;
- }
- state = IN_WORD;
- b[l++]=c;
- continue;
-
- case IN_STRING:
- if (c == '"') {
- if (buffer[(p - buffer)-1] == '\\') {
- b[l-1]=c;
- continue;
- }
- RESET_STATE();
- } else {
- b[l++]=c;
- }
- continue;
+/* {{{ */
+PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) {
+ if (param && param->type) {
+ switch (param->type) {
+ case STR_PARAM:
+ fprintf(stderr, "%s STR_PARAM(%s=%lu)\n", msg, param->str, param->len);
+ break;
+
+ case ADDR_PARAM:
+ fprintf(stderr, "%s ADDR_PARAM(%lu)\n", msg, param->addr);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ fprintf(stderr, "%s NUMERIC_FILE_PARAM(%s:#%lu)\n", msg, param->file.name, param->file.line);
+ break;
+
+ case FILE_PARAM:
+ fprintf(stderr, "%s FILE_PARAM(%s:%lu)\n", msg, param->file.name, param->file.line);
+ break;
+
+ case METHOD_PARAM:
+ fprintf(stderr, "%s METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ fprintf(stderr, "%s NUMERIC_METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ fprintf(stderr, "%s NUMERIC_FUNCTION_PARAM(%s::%ld)\n", msg, param->str, param->num);
+ break;
+
+ case NUMERIC_PARAM:
+ fprintf(stderr, "%s NUMERIC_PARAM(%ld)\n", msg, param->num);
+ break;
+
+ case COND_PARAM:
+ fprintf(stderr, "%s COND_PARAM(%s=%lu)\n", msg, param->str, param->len);
+ break;
+
+ case OP_PARAM:
+ fprintf(stderr, "%s OP_PARAM(%s=%lu)\n", msg, param->str, param->len);
+ break;
+
+ default: {
+ /* not yet */
+ }
+ }
+ }
+} /* }}} */
- case IN_WORD:
- if (isspace(c)) {
- RESET_STATE();
- } else {
- b[l++]=c;
+/* {{{ */
+PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack) {
+ if (stack && stack->next) {
+ phpdbg_param_t *remove = stack->next;
+
+ while (remove) {
+ phpdbg_param_t *next = NULL;
+
+ if (remove->next)
+ next = remove->next;
+
+ switch (remove->type) {
+ case NUMERIC_METHOD_PARAM:
+ case METHOD_PARAM:
+ if (remove->method.class)
+ free(remove->method.class);
+ if (remove->method.name)
+ free(remove->method.name);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ case STR_PARAM:
+ case OP_PARAM:
+ if (remove->str)
+ free(remove->str);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ case FILE_PARAM:
+ if (remove->file.name)
+ free(remove->file.name);
+ break;
+
+ default: {
+ /* nothing */
}
- continue;
+ }
+
+ free(remove);
+ remove = NULL;
+
+ if (next)
+ remove = next;
+ else break;
}
}
+
+ stack->next = NULL;
+} /* }}} */
- switch (state) {
- case IN_WORD: {
- RESET_STATE();
- } break;
+/* {{{ */
+PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) {
+ phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t));
- case IN_STRING:
- phpdbg_error(
- "Malformed command line (unclosed quote) @ %ld: %s!",
- (p - buffer)-1, &buffer[(p - buffer)-1]);
- break;
+ if (!next)
+ return;
- case IN_BETWEEN:
- break;
+ *(next) = *(param);
+
+ next->next = NULL;
+
+ if (stack->top == NULL) {
+ stack->top = next;
+ next->top = NULL;
+ stack->next = next;
+ } else {
+ stack->top->next = next;
+ next->top = stack->top;
+ stack->top = next;
}
- if ((*argc) == 0) {
- /* not needed */
- efree(argv);
+ stack->len++;
+} /* }}} */
- /* to be sure */
- return NULL;
+PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC) {
+ if (command) {
+ char buffer[128] = {0,};
+ const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL;
+ const char *arg = command->args;
+ size_t least = 0L,
+ received = 0L,
+ current = 0L;
+ zend_bool optional = 0;
+
+ /* check for arg spec */
+ if (!(arg) || !(*arg)) {
+ if (!top) {
+ return SUCCESS;
+ }
+
+ asprintf(why,
+ "The command \"%s\" expected no arguments",
+ phpdbg_command_name(command, buffer));
+ return FAILURE;
+ }
+
+ least = 0L;
+
+ /* count least amount of arguments */
+ while (arg && *arg) {
+ if (arg[0] == '|') {
+ break;
+ }
+ least++;
+ arg++;
+ }
+
+ arg = command->args;
+
+#define verify_arg(e, a, t) if (!(a)) { \
+ if (!optional) { \
+ asprintf(why, \
+ "The command \"%s\" expected %s and got nothing at parameter %lu", \
+ phpdbg_command_name(command, buffer), \
+ (e), \
+ current); \
+ return FAILURE;\
+ } \
+} else if ((a)->type != (t)) { \
+ asprintf(why, \
+ "The command \"%s\" expected %s and got %s at parameter %lu", \
+ phpdbg_command_name(command, buffer), \
+ (e),\
+ phpdbg_get_param_type((a) TSRMLS_CC), \
+ current); \
+ return FAILURE; \
+}
+
+ while (arg && *arg) {
+ current++;
+
+ switch (*arg) {
+ case '|': {
+ current--;
+ optional = 1;
+ arg++;
+ } continue;
+
+ case 'i': verify_arg("raw input", top, STR_PARAM); break;
+ case 's': verify_arg("string", top, STR_PARAM); break;
+ case 'n': verify_arg("number", top, NUMERIC_PARAM); break;
+ case 'm': verify_arg("method", top, METHOD_PARAM); break;
+ case 'a': verify_arg("address", top, ADDR_PARAM); break;
+ case 'f': verify_arg("file:line", top, FILE_PARAM); break;
+ case 'c': verify_arg("condition", top, COND_PARAM); break;
+ case 'o': verify_arg("opcode", top, OP_PARAM); break;
+ case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break;
+
+ case '*': { /* do nothing */ } break;
+ }
+
+ if (top ) {
+ top = top->next;
+ } else break;
+
+ received++;
+ arg++;
+ }
+
+#undef verify_arg
+
+ if ((received < least)) {
+ asprintf(why,
+ "The command \"%s\" expected at least %lu arguments (%s) and received %lu",
+ phpdbg_command_name(command, buffer),
+ least,
+ command->args,
+ received);
+ return FAILURE;
+ }
}
+
+ return SUCCESS;
+}
+
+/* {{{ */
+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) {
+ const phpdbg_command_t *command = commands;
+ phpdbg_param_t *name = *top;
+ const phpdbg_command_t *matched[3] = {NULL, NULL, NULL};
+ ulong matches = 0L;
+
+ while (command && command->name && command->handler) {
+ if ((name->len == 1) || (command->name_len >= name->len)) {
+ /* match single letter alias */
+ if (command->alias && (name->len == 1)) {
+ if (command->alias == (*name->str)) {
+ matched[matches] = command;
+ matches++;
+ }
+ } else {
- return argv;
+ /* match full, case insensitive, command name */
+ if (strncasecmp(command->name, name->str, name->len) == SUCCESS) {
+ if (matches < 3) {
+
+ /* only allow abbreviating commands that can be aliased */
+ if (((name->len != command->name_len) && command->alias) ||
+ (name->len == command->name_len)) {
+ matched[matches] = command;
+ matches++;
+ }
+
+
+ /* exact match */
+ if (name->len == command->name_len)
+ break;
+ } else break;
+ }
+ }
+ }
+
+ command++;
+ }
+
+ switch (matches) {
+ case 0: {
+ if (parent) {
+ asprintf(
+ why,
+ "The command \"%s %s\" could not be found",
+ parent->name, name->str);
+ } else asprintf(
+ why,
+ "The command \"%s\" could not be found",
+ name->str);
+ } return parent;
+
+ case 1: {
+ (*top) = (*top)->next;
+
+ command = matched[0];
+ } break;
+
+ default: {
+ char *list = NULL;
+ zend_uint it = 0;
+ size_t pos = 0;
+
+ while (it < matches) {
+ if (!list) {
+ list = malloc(
+ matched[it]->name_len + 1 +
+ ((it+1) < matches ? sizeof(", ")-1 : 0));
+ } else {
+ list = realloc(list,
+ (pos + matched[it]->name_len) + 1 +
+ ((it+1) < matches ? sizeof(", ")-1 : 0));
+ }
+ memcpy(&list[pos], matched[it]->name, matched[it]->name_len);
+ pos += matched[it]->name_len;
+ if ((it+1) < matches) {
+ memcpy(&list[pos], ", ", sizeof(", ")-1);
+ pos += (sizeof(", ") - 1);
+ }
+
+ list[pos] = 0;
+ it++;
+ }
+
+ asprintf(
+ why,
+ "The command \"%s\" is ambigious, matching %lu commands (%s)",
+ name->str, matches, list);
+ free(list);
+ } return NULL;
+ }
+
+ if (command->subs && (*top) && ((*top)->type == STR_PARAM)) {
+ return phpdbg_stack_resolve(command->subs, command, top, why);
+ } else {
+ return command;
+ }
+
+ return NULL;
} /* }}} */
-PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
+/* {{{ */
+PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC) {
+ phpdbg_param_t *top = NULL;
+ const phpdbg_command_t *handler = NULL;
+
+ if (stack->type != STACK_PARAM) {
+ asprintf(
+ why, "The passed argument was not a stack !!");
+ return FAILURE;
+ }
+
+ if (!stack->len) {
+ asprintf(
+ why, "The stack contains nothing !!");
+ return FAILURE;
+ }
+
+ top = (phpdbg_param_t*) stack->next;
+
+ switch (top->type) {
+ case EVAL_PARAM:
+ return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC);
+
+ case RUN_PARAM:
+ return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC);
+
+ case SHELL_PARAM:
+ return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC);
+
+ case STR_PARAM: {
+ handler = phpdbg_stack_resolve(
+ phpdbg_prompt_commands, NULL, &top, why);
+
+ if (handler) {
+ if (phpdbg_stack_verify(handler, &top, why TSRMLS_CC) == SUCCESS) {
+ return handler->handler(top TSRMLS_CC);
+ }
+ }
+ } return FAILURE;
+
+ default:
+ asprintf(
+ why, "The first parameter makes no sense !!");
+ return FAILURE;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
{
- phpdbg_input_t *buffer = NULL;
char *cmd = NULL;
#ifndef HAVE_LIBREADLINE
char buf[PHPDBG_MAX_CMD];
#endif
+ char *buffer = NULL;
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
@@ -513,32 +847,8 @@ readline:
}
#endif
} else cmd = buffered;
-
- /* allocate and sanitize buffer */
- buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
- if (!buffer) {
- return NULL;
- }
-
- buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length);
-
- /* store constant pointer to start of buffer */
- buffer->start = (char* const*) buffer->string;
-
- buffer->argv = phpdbg_read_argv(
- buffer->string, &buffer->argc TSRMLS_CC);
-
-#ifdef PHPDBG_DEBUG
- if (buffer->argc) {
- int arg = 0;
-
- while (arg < buffer->argc) {
- phpdbg_debug(
- "argv %d=%s", arg, buffer->argv[arg]->string);
- arg++;
- }
- }
-#endif
+
+ buffer = estrdup(cmd);
#ifdef HAVE_LIBREADLINE
if (!buffered && cmd &&
@@ -546,144 +856,34 @@ readline:
free(cmd);
}
#endif
-
- return buffer;
}
- return NULL;
-} /* }}} */
+ if (buffer && isspace(*buffer)) {
+ char *trimmed = buffer;
+ while (isspace(*trimmed))
+ trimmed++;
-PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */
-{
- if (argv) {
- if (argc) {
- int arg;
- for (arg=0; arg<argc; arg++) {
- phpdbg_destroy_input(
- &argv[arg] TSRMLS_CC);
- }
- }
- efree(argv);
+ trimmed = estrdup(trimmed);
+ efree(buffer);
+ buffer = trimmed;
}
-} /* }}} */
-
-PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
-{
- if (*input) {
- if ((*input)->string) {
- efree((*input)->string);
+ if (buffer && strlen(buffer)) {
+ if (PHPDBG_G(buffer)) {
+ efree(PHPDBG_G(buffer));
+ }
+ PHPDBG_G(buffer) = estrdup(buffer);
+ } else {
+ if (PHPDBG_G(buffer)) {
+ buffer = estrdup(PHPDBG_G(buffer));
}
-
- phpdbg_destroy_argv(
- (*input)->argv, (*input)->argc TSRMLS_CC);
-
- efree(*input);
}
+
+ return buffer;
} /* }}} */
-PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */
{
- int rc = FAILURE;
-
- if (input->argc > 0) {
- while (command && command->name && command->handler) {
- if (((command->name_len == input->argv[0]->length) &&
- (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) ||
- (command->alias &&
- (input->argv[0]->length == 1) &&
- (command->alias == *input->argv[0]->string))) {
-
- phpdbg_param_t param;
- phpdbg_command_t *initial_last_cmd;
- phpdbg_param_t initial_last_param;
-
- param.type = EMPTY_PARAM;
-
- if (input->argc > 1) {
- if (command->subs) {
- phpdbg_input_t sub = *input;
-
- sub.string += input->argv[0]->length;
- sub.length -= input->argv[0]->length;
-
- sub.string = phpdbg_trim(
- sub.string, sub.length, &sub.length);
-
- sub.argc--;
- sub.argv++;
-
- phpdbg_debug(
- "trying sub commands in \"%s\" for \"%s\" with %d arguments",
- command->name, sub.argv[0]->string, sub.argc-1);
-
- if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) {
- efree(sub.string);
- return SUCCESS;
- }
-
- efree(sub.string);
- }
-
- /* no sub command found */
- {
- char *store = input->string;
-
- input->string += input->argv[0]->length;
- input->length -= input->argv[0]->length;
-
- input->string = phpdbg_trim(
- input->string, input->length, &input->length);
-
- efree(store);
- }
-
- /* pass parameter on */
- phpdbg_parse_param(
- input->string,
- input->length,
- &param TSRMLS_CC);
- }
-
- phpdbg_debug(
- "found command %s for %s with %d arguments",
- command->name, input->argv[0]->string, input->argc-1);
- {
- int arg;
- for (arg=1; arg<input->argc; arg++) {
- phpdbg_debug(
- "\t#%d: [%s=%zu]",
- arg,
- input->argv[arg]->string,
- input->argv[arg]->length);
- }
- }
-
- initial_last_param = PHPDBG_G(lparam);
- initial_last_cmd = (phpdbg_command_t *)PHPDBG_G(lcmd);
- PHPDBG_G(lparam) = param;
- PHPDBG_G(lcmd) = (phpdbg_command_t *)command;
-
- rc = command->handler(&param, input TSRMLS_CC);
-
- /* only set last command when it is worth it! */
- if (rc != FAILURE && !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
- phpdbg_clear_param(&initial_last_param TSRMLS_CC);
- } else if (PHPDBG_G(lcmd) == command && !memcmp(&PHPDBG_G(lparam),& initial_last_param, sizeof(phpdbg_param_t))) {
- PHPDBG_G(lparam) = initial_last_param;
- PHPDBG_G(lcmd) = initial_last_cmd;
- phpdbg_clear_param(&param TSRMLS_CC);
- }
- break;
- }
- command++;
- }
- } else {
- /* this should NEVER happen */
- phpdbg_error(
- "No function executed!!");
- }
-
- return rc;
+ efree(*input);
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_cmd.h b/sapi/phpdbg/phpdbg_cmd.h
index c86f92bb47..571d065f59 100644
--- a/sapi/phpdbg/phpdbg_cmd.h
+++ b/sapi/phpdbg/phpdbg_cmd.h
@@ -23,8 +23,6 @@
#include "TSRM.h"
-typedef struct _phpdbg_command_t phpdbg_command_t;
-
/* {{{ Command and Parameter */
enum {
NO_ARG = 0,
@@ -36,24 +34,23 @@ typedef enum {
EMPTY_PARAM = 0,
ADDR_PARAM,
FILE_PARAM,
+ NUMERIC_FILE_PARAM,
METHOD_PARAM,
STR_PARAM,
NUMERIC_PARAM,
NUMERIC_FUNCTION_PARAM,
- NUMERIC_METHOD_PARAM
+ NUMERIC_METHOD_PARAM,
+ STACK_PARAM,
+ EVAL_PARAM,
+ SHELL_PARAM,
+ COND_PARAM,
+ OP_PARAM,
+ ORIG_PARAM,
+ RUN_PARAM
} phpdbg_param_type;
-typedef struct _phpdbg_input_t phpdbg_input_t;
-
-struct _phpdbg_input_t {
- char * const *start;
- char *string;
- size_t length;
- phpdbg_input_t **argv;
- int argc;
-};
-
-typedef struct _phpdbg_param {
+typedef struct _phpdbg_param phpdbg_param_t;
+struct _phpdbg_param {
phpdbg_param_type type;
long num;
zend_ulong addr;
@@ -67,10 +64,31 @@ typedef struct _phpdbg_param {
} method;
char *str;
size_t len;
-} phpdbg_param_t;
+ phpdbg_param_t *next;
+ phpdbg_param_t *top;
+};
-typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t*, const phpdbg_input_t* TSRMLS_DC);
+#define phpdbg_init_param(v, t) do{ \
+ (v)->type = (t); \
+ (v)->addr = 0; \
+ (v)->num = 0; \
+ (v)->file.name = NULL; \
+ (v)->file.line = 0; \
+ (v)->method.class = NULL; \
+ (v)->method.name = NULL; \
+ (v)->str = NULL; \
+ (v)->len = 0; \
+ (v)->next = NULL; \
+ (v)->top = NULL; \
+} while(0)
+
+#ifndef YYSTYPE
+#define YYSTYPE phpdbg_param_t
+#endif
+
+typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t* TSRMLS_DC);
+typedef struct _phpdbg_command_t phpdbg_command_t;
struct _phpdbg_command_t {
const char *name; /* Command name */
size_t name_len; /* Command name length */
@@ -79,7 +97,8 @@ struct _phpdbg_command_t {
char alias; /* Alias */
phpdbg_command_handler_t handler; /* Command handler */
const phpdbg_command_t *subs; /* Sub Commands */
- char arg_type; /* Accept args? */
+ char *args; /* Argument Spec */
+ const phpdbg_command_t *parent; /* Parent Command */
};
/* }}} */
@@ -95,35 +114,29 @@ typedef struct {
} phpdbg_frame_t;
/* }}} */
-
-
/*
* Workflow:
-* 1) read input
-* input takes the line from console, creates argc/argv
-* 2) parse parameters into suitable types based on arg_type
-* takes input from 1) and arg_type and creates parameters
-* 3) do command
-* executes commands
-* 4) destroy parameters
-* cleans up what was allocated by creation of parameters
-* 5) destroy input
-* cleans up what was allocated by creation of input
+* 1) the lexer/parser creates a stack of commands and arguments from input
+* 2) the commands at the top of the stack are resolved sensibly using aliases, abbreviations and case insensitive matching
+* 3) the remaining arguments in the stack are verified (optionally) against the handlers declared argument specification
+* 4) the handler is called passing the top of the stack as the only parameter
+* 5) the stack is destroyed upon return from the handler
*/
/*
* Input Management
*/
-PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
-PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC);
+PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC);
+PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC);
-/*
-* Argument Management
-*/
-PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC);
-PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC);
-#define phpdbg_argv_is(n, s) \
- (memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS)
+/**
+ * Stack Management
+ */
+PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param);
+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);
+PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC);
+PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC);
+PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack);
/*
* Parameter Management
@@ -135,28 +148,27 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_par
PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC);
-
-/*
-* Command Executor
-*/
-PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t*, phpdbg_input_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg);
/**
* Command Declarators
*/
#define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name
-#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, has_args) \
- {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, has_args}
+#define PHPDBG_COMMAND_D_EXP(name, tip, alias, handler, children, args, parent) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent}
+
+#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL}
-#define PHPDBG_COMMAND_D(name, tip, alias, children, has_args) \
- {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, has_args}
+#define PHPDBG_COMMAND_D(name, tip, alias, children, args) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL}
-#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC)
+#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC)
-#define PHPDBG_COMMAND_ARGS param, input TSRMLS_CC
+#define PHPDBG_COMMAND_ARGS param TSRMLS_CC
-#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0'}
+#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL}
/*
* Default Switch Case
diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c
index de02addc1b..a235fe8cb0 100644
--- a/sapi/phpdbg/phpdbg_frame.c
+++ b/sapi/phpdbg/phpdbg_frame.c
@@ -167,7 +167,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
zval **tmp;
zval **file, **line;
HashPosition position;
- int i = 1, limit = num;
+ int i = 0, limit = num;
int user_defined;
if (limit < 0) {
@@ -186,7 +186,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace),
(void**)&tmp, &position) == FAILURE) {
- phpdbg_write("frame #0: {main} at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ phpdbg_write("frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line));
break;
}
diff --git a/sapi/phpdbg/phpdbg_help.c b/sapi/phpdbg/phpdbg_help.c
index d2fea8d7c7..f2d074ded6 100644
--- a/sapi/phpdbg/phpdbg_help.c
+++ b/sapi/phpdbg/phpdbg_help.c
@@ -15,589 +15,916 @@
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+ | Authors: Terry Ellison <terry@ellisons.org.uk> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_help.h"
-#include "phpdbg_print.h"
-#include "phpdbg_utils.h"
-#include "phpdbg_break.h"
-#include "phpdbg_list.h"
-#include "phpdbg_info.h"
-#include "phpdbg_set.h"
+#include "phpdbg_prompt.h"
+#include "zend.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-PHPDBG_HELP(exec) /* {{{ */
+/* {{{ Commands Table */
+#define PHPDBG_COMMAND_HELP_D(name, tip, alias, action) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, action, NULL, 0}
+
+const phpdbg_command_t phpdbg_help_commands[] = {
+ PHPDBG_COMMAND_HELP_D(aliases, "show alias list", 'a', phpdbg_do_help_aliases),
+ PHPDBG_COMMAND_HELP_D(options, "command line options", 0, NULL),
+ PHPDBG_COMMAND_HELP_D(overview, "help overview", 0, NULL),
+ PHPDBG_COMMAND_HELP_D(phpdbginit, "phpdbginit file format", 0, NULL),
+ PHPDBG_COMMAND_HELP_D(syntax, "syntax overview", 0, NULL),
+ PHPDBG_END_COMMAND
+}; /* }}} */
+
+/* {{{ pretty_print. Formatting escapes and wrapping text in a string before printing it. */
+void pretty_print(char *text TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("\tWill attempt execution, if compilation has not yet taken place, it occurs now");
- phpdbg_writeln("The execution context must be set before execution can take place");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ char *new, *p, *q;
+
+ const char *prompt_escape = phpdbg_get_prompt(TSRMLS_C);
+ unsigned int prompt_escape_len = strlen(prompt_escape);
+ unsigned int prompt_len = strlen(PHPDBG_G(prompt)[0]);
+
+ const char *bold_on_escape = PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "\033[1m" : "";
+ const char *bold_off_escape = PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "\033[0m" : "";
+ unsigned int bold_escape_len = strlen(bold_on_escape);
+
+ unsigned int term_width = phpdbg_get_terminal_width(TSRMLS_C);
+ unsigned int size = 0;
+
+ int in_bold = 0;
+
+ char *last_new_blank = NULL; /* position in new buffer of last blank char */
+ unsigned int last_blank_count = 0; /* printable char offset of last blank char */
+ unsigned int line_count = 0; /* number printable chars on current line */
+
+ /* First pass calculates a safe size for the pretty print version */
+ for (p = text; *p; p++) {
+ if (UNEXPECTED(p[0] == '*') && p[1] == '*') {
+ size += bold_escape_len - 2;
+ p++;
+ } else if (UNEXPECTED(p[0] == '$') && p[1] == 'P') {
+ size += prompt_escape_len - 2;
+ p++;
+ } else if (UNEXPECTED(p[0] == '\\')) {
+ p++;
+ }
+ }
+ size += (p-text)+1;
+
+ new = emalloc(size);
+ /*
+ * Second pass substitutes the bold and prompt escape sequences and line wrap
+ *
+ * ** toggles bold on and off if PHPDBG_IS_COLOURED flag is set
+ * $P substitutes the prompt sequence
+ * Lines are wrapped by replacing the last blank with a CR before <term width>
+ * characters. (This defaults to 100 if the width can't be detected). In the
+ * pathelogical case where no blanks are found, then the wrap occurs at the
+ * first blank.
+ */
+ for (p = text, q = new; *p; p++) {
+ if (UNEXPECTED(*p == ' ')) {
+ last_new_blank = q;
+ last_blank_count = line_count++;
+ *q++ = ' ';
+ } else if (UNEXPECTED(*p == '\n')) {
+ last_new_blank = NULL;
+ *q++ = *p;
+ last_blank_count = 0;
+ line_count = 0;
+ } else if (UNEXPECTED(p[0] == '*') && p[1] == '*') {
+ if (bold_escape_len) {
+ in_bold = !in_bold;
+ memcpy (q, in_bold ? bold_on_escape : bold_off_escape, bold_escape_len);
+ q += bold_escape_len;
+ /* bold on/off has zero print width so line count is unchanged */
+ }
+ p++;
+ } else if (UNEXPECTED(p[0] == '$') && p[1] == 'P') {
+ memcpy (q, prompt_escape, prompt_escape_len);
+ q += prompt_escape_len;
+ line_count += prompt_len;
+ p++;
+ } else if (UNEXPECTED(p[0] == '\\')) {
+ p++;
+ *q++ = *p;
+ line_count++;
+ } else {
+ *q++ = *p;
+ line_count++;
+ }
-PHPDBG_HELP(step) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("You can enable and disable stepping at any phpdbg prompt during execution");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sstepping 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%ss 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill enable stepping");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("While stepping is enabled you are presented with a prompt after the execution of each opcode");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ if (UNEXPECTED(line_count>=term_width) && last_new_blank) {
+ *last_new_blank = '\n';
+ last_new_blank = NULL;
+ line_count -= last_blank_count;
+ last_blank_count = 0;
+ }
+ }
+ *q++ = '\0';
-PHPDBG_HELP(next) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_write("Step back into the vm and execute the next opcode");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%snext", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sn", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: is only useful while executing");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ if ((q-new)>size) {
+ phpdbg_error("Output overrun of %lu bytes", ((q-new) - size));
+ }
-PHPDBG_HELP(until) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Step back into the vm, skipping breakpoints until the next source line");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%suntil", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%su", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: is only useful while executing");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ phpdbg_write("%s\n", new);
+ efree(new);
+} /* }}} */
-PHPDBG_HELP(finish) /* {{{ */
+/* {{{ summary_print. Print a summary line giving, the command, its alias and tip */
+void summary_print(phpdbg_command_t const * const cmd TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("Step back into the vm, skipping breakpoints until past the end of the current stack");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sfinish", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sF", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: this allows all breakpoints that would otherwise break execution in the current scope to be skipped");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(leave) /* {{{ */
+ char *summary;
+ spprintf(&summary, 0, "Command: **%s** Alias: **%c** **%s**\n", cmd->name, cmd->alias, cmd->tip);
+ pretty_print(summary TSRMLS_CC);
+ efree(summary);
+}
+
+/* {{{ get_help. Retries and formats text from the phpdbg help text table */
+static char *get_help(const char * const key TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("Step back into the vm, skipping breakpoints until the current stack is returning");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sleave", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sL", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause a break when instructed to leave the current context");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: this allows inspection of the return value before it is returned");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ phpdbg_help_text_t *p;
-PHPDBG_HELP(compile) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Pre-compilation of the execution context provides the opportunity to inspect opcodes before execution");
- phpdbg_writeln("The execution context must be set for compilation to succeed");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%scompile", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sc", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill compile the current execution context, populating class/function/constant/etc tables");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: It is a good idea to clean the environment between each compilation");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ /* Note that phpdbg_help_text is not assumed to be collated in key order. This is an
+ inconvience that means that help can't be logically grouped Not worth
+ the savings */
-PHPDBG_HELP(print) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("By default, print will show information about the current execution context");
- phpdbg_writeln("Other printing commands give access to instruction information");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sprint class \\my\\class", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp c \\my\\class", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the methods in \\my\\class");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint method \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp m \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for \\my\\class::method");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint func .getSomething", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp f .getSomething", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for ::getSomething in the active scope");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint func my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp f my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the global function my_function");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint opline", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp o", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instruction for the current opline");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint exec", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp e", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the execution context");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint stack", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp s", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the current stack");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Specific printers loaded are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *print_command = phpdbg_print_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (print_command && print_command->name) {
- if (print_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", print_command->alias, print_command->name, print_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", print_command->name, print_command->tip);
- }
- ++print_command;
+ for (p = phpdbg_help_text; p->key; p++) {
+ if (!strcmp(p->key, key)) {
+ return p->text;
}
}
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(run) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Execute the current context inside the phpdbg vm");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%srun", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sr", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause execution of the context, if it is set.");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: The execution context must be set, but not necessarily compiled before execution occurs");
- phpdbg_help_footer();
- return SUCCESS;
+ return ""; /* return empty string to denote no match found */
} /* }}} */
-PHPDBG_HELP(eval) /* {{{ */
+/* {{{ get_command. Return number of matching commands from a command table.
+ * Unlike the command parser, the help search is sloppy that is partial matches can occur
+ * * Any single character key is taken as an alias.
+ * * Other keys are matched again the table on the first len characters.
+ * * This means that non-unique keys can generate multiple matches.
+ * * The first matching command is returned as an OUT parameter. *
+ * The rationale here is to assist users in finding help on commands. So unique matches
+ * will be used to generate a help message but non-unique one will be used to list alternatives.
+ */
+static int get_command(
+ const char *key, size_t len, /* pointer and length of key */
+ phpdbg_command_t const **command, /* address of first matching command */
+ phpdbg_command_t const * commands /* command table to be scanned */
+ TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("Access to eval() allows you to change the environment during execution, careful though!!");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%seval $variable", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sE $variable", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print_r($variable) on the console, if it is defined");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%seval $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sE $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill set $variable in the current scope");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: eval() will always show the result; do not prefix the code with \"return\"");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(break) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Setting a breakpoint stops execution at a specific stage");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sbreak [file] test.php:1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [F] test.php:1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break execution on line 1 of test.php");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [func] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [f] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break execution on entry to my_function");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [method] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [m] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break execution on entry to \\my\\class::method");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [address] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [a] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline with the address provided");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [address] my_function#1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [a] my_function#1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline number 1 of the function my_function");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [address] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [a] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline number 2 of the method \\my\\class::method");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak address test.php:3", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb a test.php:3", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline number 3 of test.php");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [lineno] 200", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [l] 200", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at line 200 of the currently executing file");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break when the condition evaluates to true");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak at phpdbg::isGreat if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at every opcode in phpdbg::isGreat when the condition evaluates to true");
- phpdbg_writeln("\t%sbreak at test.php:20 if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at every opcode on line 20 of test.php when the condition evaluates to true");
- phpdbg_write("\t");
- phpdbg_notice("The location can be anything accepted by file, func, method, or address break commands");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak op ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break on every occurrence of the opcode provided");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak del 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb d 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill remove the breakpoint with the given identifier");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: An address is only valid for the current compilation");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Specific breakers loaded are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *break_command = phpdbg_break_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (break_command && break_command->name) {
- if (break_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", break_command->alias, break_command->name, break_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", break_command->name, break_command->tip);
+ const phpdbg_command_t *c;
+ unsigned int num_matches = 0;
+
+ if (len == 1) {
+ for (c=commands; c->name; c++) {
+ if (c->alias == key[0]) {
+ num_matches++;
+ if ( num_matches == 1 && command) {
+ *command = c;
+ }
}
- ++break_command;
}
- }
- phpdbg_writeln("Note: Conditional breaks are costly, use them sparingly!");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(clean) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("While debugging you may experience errors because of attempts to redeclare classes, constants or functions");
- phpdbg_writeln("Cleaning the environment cleans these tables, so that files can be recompiled without exiting phpdbg");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(clear) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Clearing breakpoints means you can once again run code without interruption");
- phpdbg_writeln("Note: all breakpoints are lost; be sure debugging is complete before clearing");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(info) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("info commands provide quick access to various types of information about the PHP environment");
- phpdbg_writeln("Specific info commands are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *info_command = phpdbg_info_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (info_command && info_command->name) {
- if (info_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", info_command->alias, info_command->name, info_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", info_command->name, info_command->tip);
+ } else {
+ for (c=commands; c->name; c++) {
+ if (!strncmp(c->name, key, len)) {
+ ++num_matches;
+ if ( num_matches == 1 && command) {
+ *command = c;
+ }
}
- ++info_command;
}
}
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ return num_matches;
-PHPDBG_HELP(quiet) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Setting quietness on will stop the OPLINE output during execution");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%squiet 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sQ 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill silence OPLINE output, while");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%squiet 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sQ 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill enable OPLINE output again");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: Quietness is disabled automatically while stepping");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+} /* }}} */
-PHPDBG_HELP(back) /* {{{ */
+PHPDBG_COMMAND(help) /* {{{ */
{
- phpdbg_help_header();
- phpdbg_writeln("The backtrace is built with the default debug backtrace functionality");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sback 5", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%st 5", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill limit the number of frames to 5, the default is no limit");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: it is not necessary for an exception to be thrown to show a backtrace");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ phpdbg_command_t const *cmd;
+ int n;
-PHPDBG_HELP(frame) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("When viewing a backtrace, it is sometimes useful to jump to a frame in that trace");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sframe 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sf 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill go to frame 2, temporarily affecting scope and allowing access to the variables in that frame");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: the current frame is restored when execution continues");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ if (!param || param->type == EMPTY_PARAM) {
+ pretty_print(get_help("overview!" TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+ }
-PHPDBG_HELP(list) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("The list command displays source code for the given argument");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%slist [lines] 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [l] 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print next 2 lines from the current file");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist [func] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [f] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of the global function \"my_function\"");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist [func] .mine", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [f] .mine", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of the method \"mine\" from the active scope");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist [method] my::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [m] my::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of \"my::method\"");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist c myClass", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl c myClass", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of \"myClass\"");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: before listing functions you must have a populated function table, try compile!!");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Specific listers loaded are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *list_command = phpdbg_list_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (list_command && list_command->name) {
- if (list_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", list_command->alias, list_command->name, list_command->tip);
+ if (param && param->type == STR_PARAM) {
+ n = get_command(param->str, param->len, &cmd, phpdbg_prompt_commands TSRMLS_CC);
+
+ if (n==1) {
+ summary_print(cmd TSRMLS_CC);
+ pretty_print(get_help(cmd->name TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+
+ } else if (n>1) {
+ if (param->len > 1) {
+ for (cmd=phpdbg_prompt_commands; cmd->name; cmd++) {
+ if (!strncmp(cmd->name, param->str, param->len)) {
+ summary_print(cmd TSRMLS_CC);
+ }
+ }
+ pretty_print(get_help("duplicate!" TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
} else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", list_command->name, list_command->tip);
+ phpdbg_error("Internal help error, non-unique alias \"%c\"", param->str[0]);
+ return FAILURE;
+ }
+
+ } else { /* no prompt command found so try help topic */
+ n = get_command( param->str, param->len, &cmd, phpdbg_help_commands TSRMLS_CC);
+
+ if (n>0) {
+ if (cmd->alias == 'a') { /* help aliases executes a canned routine */
+ return cmd->handler(param TSRMLS_CC);
+ } else {
+ pretty_print(get_help(cmd->name TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+ }
}
- ++list_command;
}
}
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-PHPDBG_HELP(oplog) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Even when quietness is enabled you may wish to save opline logs to a file");
- phpdbg_writeln("Setting a new oplog closes the previously open log");
- phpdbg_writeln("The log includes a high resolution timestamp on each entry");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%soplog /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sO /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill open the file /path/to/my.oplog for writing, creating it if it does not exist");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%soplog 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sO 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill close the currently open log file, disabling oplog");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: upon failure to open a new oplog, the last oplog is held open");
- phpdbg_help_footer();
- return SUCCESS;
+ return FAILURE;
+
} /* }}} */
-PHPDBG_HELP(set) /* {{{ */
+PHPDBG_HELP(aliases) /* {{{ */
{
- phpdbg_help_header();
- phpdbg_writeln("Configure how phpdbg looks and behaves with the set command");
- phpdbg_writeln("Specific set commands are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *set_command = phpdbg_set_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (set_command && set_command->name) {
- if (set_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", set_command->alias, set_command->name, set_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", set_command->name, set_command->tip);
+ const phpdbg_command_t *c, *c_sub;
+ int len;
+
+ /* Print out aliases for all commands except help as this one comes last */
+ phpdbg_writeln("Below are the aliased, short versions of all supported commands");
+ for(c = phpdbg_prompt_commands; c->name; c++) {
+ if (c->alias && c->alias != 'h') {
+ phpdbg_writeln(" %c %-20s %s", c->alias, c->name, c->tip);
+ if (c->subs) {
+ len = 20 - 1 - c->name_len;
+ for(c_sub = c->subs; c_sub->alias; c_sub++) {
+ if (c_sub->alias) {
+ phpdbg_writeln(" %c %c %s %-*s %s",
+ c->alias, c_sub->alias, (char *)c->name, len, c_sub->name, c_sub->tip);
+ }
+ }
}
- ++set_command;
}
}
-#ifndef _WIN32
- phpdbg_notice("Colors");
- {
- const phpdbg_color_t *color = phpdbg_get_colors(TSRMLS_C);
-
- if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
- phpdbg_writeln("\t%-20s\t\tExample", "Name");
- } else {
- phpdbg_writeln("\tName");
- }
-
- while (color && color->name) {
- if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
- phpdbg_writeln(
- "\t%-20s\t\t\033[%smphpdbg rocks :)\033[0m", color->name, color->code);
- } else {
- phpdbg_writeln("\t%s", color->name);
- }
- ++color;
+
+ /* Print out aliases for help as this one comes last, with the added text on how aliases are used */
+ get_command("h", 1, &c, phpdbg_prompt_commands TSRMLS_CC);
+ phpdbg_writeln(" %c %-20s %s\n", c->alias, c->name, c->tip);
+
+ len = 20 - 1 - c->name_len;
+ for(c_sub = c->subs; c_sub->alias; c_sub++) {
+ if (c_sub->alias) {
+ phpdbg_writeln(" %c %c %s %-*s %s",
+ c->alias, c_sub->alias, c->name, len, c_sub->name, c_sub->tip);
}
}
- phpdbg_writeln("The <element> for set color can be \"prompt\", \"notice\", or \"error\"");
-#endif
- phpdbg_help_footer();
+
+ pretty_print(get_help("aliases!" TSRMLS_CC) TSRMLS_CC);
return SUCCESS;
} /* }}} */
-PHPDBG_HELP(register) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Register any global function for use as a command in phpdbg console");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sregister scandir", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sR scandir", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill register the scandir function for use in phpdbg");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: arguments passed as strings, return (if present) print_r'd on console");
- if (zend_hash_num_elements(&PHPDBG_G(registered))) {
- HashPosition position;
- char *name = NULL;
- zend_uint name_len = 0;
-
- phpdbg_notice("Registered Functions (%d)", zend_hash_num_elements(&PHPDBG_G(registered)));
- for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(registered), &position);
- zend_hash_get_current_key_ex(&PHPDBG_G(registered), &name, &name_len, NULL, 1, &position) == HASH_KEY_IS_STRING;
- zend_hash_move_forward_ex(&PHPDBG_G(registered), &position)) {
- phpdbg_writeln("|-------> %s", name);
- efree(name);
- }
- }
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+/* {{{ Help Text Table
+ * Contains help text entries keyed by a lowercase ascii key.
+ * Text is in ascii and enriched by a simple markup:
+ * ** toggles bold font emphasis.
+ * $P insert an bold phpdbg> prompt.
+ * \ escapes the following character. Note that this is itself escaped inside string
+ * constants so \\\\ is required to output a single \ e.g. as in namespace names.
+ *
+ * Text will be wrapped according to the STDOUT terminal width, so paragraphs are
+ * flowed using the C stringizing and the CR definition. Also note that entries
+ * are collated in alphabetic order on key.
+ *
+ * Also note the convention that help text not directly referenceable as a help param
+ * has a key ending in !
+ */
+#define CR "\n"
+phpdbg_help_text_t phpdbg_help_text[] = {
+
+/******************************** General Help Topics ********************************/
+{"overview!", CR
+"**phpdbg** is a lightweight, powerful and easy to use debugging platform for PHP5.4+" CR
+"It supports the following commands:" CR CR
+
+"**Information**" CR
+" **list** list PHP source" CR
+" **info** displays information on the debug session" CR
+" **print** show opcodes " CR
+" **frame** select a stack frame and print a stack frame summary" CR
+" **help** provide help on a topic" CR CR
+
+"**Starting and Stopping Execution**" CR
+" **exec** set execution context" CR
+" **run** attempt execution" CR
+" **step** continue execution until other line is reached" CR
+" **continue** continue execution" CR
+" **until** continue execution up to the given location" CR
+" **finish** continue up to end of the current execution frame" CR
+" **leave** continue up to end of the current execution frame and halt after the calling instruction" CR
+" **break** set a breakpoint at the specified target" CR
+" **watch** set a watchpoint on $variable" CR
+" **clear** clear one or all breakpoints" CR
+" **clean** clean the execution environment" CR CR
+
+"**Miscellaneous**" CR
+" **set** set the phpdbg configuration" CR
+" **source** execute a phpdbginit script" CR
+" **register** register a phpdbginit function as a command alias" CR
+" **sh** shell a command" CR
+" **ev** evaluate some code" CR
+" **quit** exit phpdbg" CR CR
+
+"Type **help <command>** or (**help alias**) to get detailed help on any of the above commands, "
+"for example **help list** or **h l**. Note that help will also match partial commands if unique "
+"(and list out options if not unique), so **help clea** will give help on the **clean** command, "
+"but **help cl** will list the summary for **clean** and **clear**." CR CR
+
+"Type **help aliases** to show a full alias list, including any registered phpdginit functions" CR
+"Type **help syntax** for a general introduction to the command syntax." CR
+"Type **help options** for a list of phpdbg command line options." CR
+"Type **help phpdbginit** to show how to customise the debugger environment."
+},
+{"options", CR
+"Below are the command line options supported by phpdbg" CR CR
+ /* note the extra 4 space index in because of the extra **** */
+"**Command Line Options and Flags**" CR
+" **Option** **Example Argument** **Description**" CR
+" **-c** **-c**/my/php.ini Set php.ini file to load" CR
+" **-d** **-d**memory_limit=4G Set a php.ini directive" CR
+" **-n** Disable default php.ini" CR
+" **-q** Supress welcome banner" CR
+" **-v** Enable oplog output" CR
+" **-s** Enable stepping" CR
+" **-b** Disable colour" CR
+" **-i** **-i**my.init Set .phpdbginit file" CR
+" **-I** Ignore default .phpdbginit" CR
+" **-O** **-O**my.oplog Sets oplog output file" CR
+" **-r** Run execution context" CR
+" **-rr** Run execution context and quit after execution" CR
+" **-E** Enable step through eval, careful!" CR
+" **-S** **-S**cli Override SAPI name, careful!" CR
+" **-l** **-l**4000 Setup remote console ports" CR
+" **-a** **-a**192.168.0.3 Setup remote console bind address" CR
+" **-V** Print version number" CR
+" **--** **--** arg1 arg2 Use to delimit phpdbg arguments and php $argv; append any $argv "
+"argument after it" CR CR
+
+"**Remote Console Mode**" CR CR
+
+"This mode is enabled by specifying the **-a** option. Phpdbg will bind only to the loopback "
+"interface by default, and this can only be overridden by explicitly setting the remote console "
+"bind address using the **-a** option. If **-a** is specied without an argument, then phpdbg "
+"will bind to all available interfaces. You should be aware of the security implications of "
+"doing this, so measures should be taken to secure this service if bound to a publicly accessible "
+"interface/port." CR CR
+
+"Specify both stdin and stdout with -lstdin/stdout; by default stdout is stdin * 2."
+},
+
+{"phpdbginit", CR
+"Phpdgb uses an debugger script file to initialize the debugger context. By default, phpdbg looks "
+"for the file named **.phpdbginit** in the current working directory. This location can be "
+"overridden on the command line using the **-i** switch (see **help options** for a more "
+"details)." CR CR
+
+"Debugger scripts can also be executed using the **source** command." CR CR
+
+"A script file can contain a sequence of valid debugger commands, comments and embedded PHP "
+"code. " CR CR
+
+"Comment lines are prefixed by the **#** character. Note that comments are only allowed in script "
+"files and not in interactive sessions." CR CR
+
+"PHP code is delimited by the start and end escape tags **<:** and **:>**. PHP code can be used "
+"to define application context for a debugging session and also to extend the debugger by defining "
+"and **register** PHP functions as new commands." CR CR
+
+"Also note that executing a **clear** command will cause the current **phpdbginit** to be reparsed "
+"/ reloaded."
+},
+
+{"syntax", CR
+"Commands start with a keyword, and some (**break**, "
+"**info**, **set**, **print** and **list**) may include a subcommand keyword. All keywords are "
+"lower case but also have a single letter alias that may be used as an alternative to typing in the"
+"keyword in full. Note some aliases are uppercase, and that keywords cannot be abbreviated other "
+"than by substitution by the alias." CR CR
+
+"Some commands take an argument. Arguments are typed according to their format:" CR
+" * **omitted**" CR
+" * **address** **0x** followed by a hex string" CR
+" * **number** an optionally signed number" CR
+" * **method** a valid **Class::methodName** expression" CR
+" * **func#op** a valid **Function name** follow by # and an integer" CR
+" * **method#op** a valid **Class::methodName** follow by # and an integer" CR
+" * **string** a general string" CR
+" * **function** a valid **Function name**" CR
+" * **file:line** a valid **filename** follow by : and an integer" CR CR
+
+"In some cases the type of the argument enables the second keyword to be omitted." CR CR
+
+"Type **help** for an overview of all commands and type **help <command>** to get detailed help "
+"on any specific command." CR CR
+
+"**Valid Examples**" CR CR
+
+" $P quit" CR
+" $P q" CR
+" Quit the debugger" CR CR
+
+" $P ev $total[2]" CR
+" Evaluate and print the variable $total[2] in the current stack frame" CR
+" " CR
+" $P break 200" CR
+" $P b my_source.php:200" CR
+" Break at line 200 in the current source and in file **my_source.php**. " CR CR
+
+" $P b @ ClassX::get_args if $arg[0] == \"fred\"" CR
+" $P b ~ 3" CR
+" Break at ClassX::get_args() if $arg[0] == \"fred\" and delete breakpoint 3" CR CR
+
+"**Examples of invalid commands**" CR
+
+" $P #This is a comment" CR
+" Comments introduced by the **#** character are only allowed in **phpdbginit** script files."
+},
+
+/******************************** Help Codicils ********************************/
+{"aliases!", CR
+"Note that aliases can be used for either command or sub-command keywords or both, so **info b** "
+"is a synomyn for **info break** and **l func** for **list func**, etc." CR CR
+
+"Note that help will also accept any alias as a parameter and provide help on that command, for example **h p** will provide help on the print command."
+},
+
+{"duplicate!", CR
+"Parameter is not unique. For detailed help select help on one of the above commands."
+},
+
+/******************************** Help on Commands ********************************/
+{"back",
+"Provide a formatted backtrace using the standard debug_backtrace() functionality. An optional "
+"unsigned integer argument specifying the maximum number of frames to be traced; if omitted then "
+"a complete backtrace is given." CR CR
+
+"**Examples**" CR CR
+" $P back 5" CR
+" $P t " CR
+" " CR
+"A backtrace can be executed at any time during execution."
+},
+
+{"break",
+"Breakpoints can be set at a range of targets within the execution environment. Execution will "
+"be paused if the program flow hits a breakpoint. The break target can be one of the following "
+"types:" CR CR
+
+" **Target** **Alias** **Purpose**" CR
+" **at** **A** specify breakpoint by location and condition" CR
+" **del** **d** delete breakpoint by breakpoint identifier number" CR CR
+
+"**Break at** takes two arguments. The first is any valid target. The second "
+"is a valid PHP expression which will trigger the break in "
+"execution, if evaluated as true in a boolean context at the specified target." CR CR
-PHPDBG_HELP(source) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Sourcing a phpdbginit during your debugging session might save some time");
- phpdbg_writeln("The source command can also be used to export breakpoints to a phpdbginit file");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%ssource /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%s. /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill execute the phpdbginit file at /my/init");
- phpdbg_writeln("\t%ssource export /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%s. export /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill export breakpoints to /my/init in phpdbginit file format");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+"Note that breakpoints can also be disabled and re-enabled by the **set break** command." CR CR
+
+"**Examples**" CR CR
+" $P break test.php:100" CR
+" $P b test.php:100" CR
+" Break execution at line 100 of test.php" CR CR
+
+" $P break 200" CR
+" $P b 200" CR
+" Break execution at line 200 of the currently PHP script file" CR CR
+
+" $P break \\\\mynamespace\\\\my_function" CR
+" $P b \\\\mynamespace\\\\my_function" CR
+" Break execution on entry to \\\\mynamespace\\\\my_function" CR CR
+
+" $P break classX::method" CR
+" $P b classX::method" CR
+" Break execution on entry to classX::method" CR CR
+
+" $P break 0x7ff68f570e08" CR
+" $P b 0x7ff68f570e08" CR
+" Break at the opline at the address 0x7ff68f570e08" CR CR
-PHPDBG_HELP(shell) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Direct access to shell commands saves having to switch windows/consoles");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sshell ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%s- ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill execute ls /usr/src/php-src, displaying the output in the console");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: read only commands please!");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+" $P break my_function#14" CR
+" $P b my_function#14" CR
+" Break at the opline #14 of the function my_function" CR CR
-PHPDBG_HELP(options) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Below are the command line options supported by phpdbg");
- phpdbg_notice("Command Line Options and Flags");
- phpdbg_writeln(" -c\t-c/my/php.ini\t\tSet php.ini file to load");
- phpdbg_writeln(" -d\t-dmemory_limit=4G\tSet a php.ini directive");
- phpdbg_writeln(" -n\tN/A\t\t\tDisable default php.ini");
- phpdbg_writeln(" -q\tN/A\t\t\tSuppress welcome banner");
- phpdbg_writeln(" -e\t-emytest.php\t\tSet execution context");
- phpdbg_writeln(" -v\tN/A\t\t\tEnable oplog output");
- phpdbg_writeln(" -s\tN/A\t\t\tEnable stepping");
- phpdbg_writeln(" -b\tN/A\t\t\tDisable colour");
- phpdbg_writeln(" -i\t-imy.init\t\tSet .phpdbginit file");
- phpdbg_writeln(" -I\tN/A\t\t\tIgnore default .phpdbginit");
- phpdbg_writeln(" -O\t-Omy.oplog\t\tSets oplog output file");
- phpdbg_writeln(" -r\tN/A\t\t\tRun execution context");
- phpdbg_writeln(" -E\tN/A\t\t\tEnable step through eval, careful!");
- phpdbg_writeln(" -S\t-Scli\t\t\tOverride SAPI name, careful!");
-#ifndef _WIN32
- phpdbg_writeln(" -l\t-l4000\t\t\tSetup remote console ports");
- phpdbg_writeln(" -a\t-a192.168.0.3\t\tSetup remote console bind address");
-#endif
- phpdbg_writeln(" -V\tN/A\t\t\tVersion number");
- phpdbg_notice("Passing -rr will quit automatically after execution");
-#ifndef _WIN32
- phpdbg_writeln("Remote Console Mode");
- phpdbg_notice("For security, phpdbg will bind only to the loopback interface by default");
- phpdbg_writeln("-a without an argument implies all; phpdbg will bind to all available interfaces.");
- phpdbg_writeln("specify both stdin and stdout with -lstdin/stdout; by default stdout is stdin * 2.");
- phpdbg_notice("Steps should be taken to secure this service if bound to a public interface/port");
-#endif
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+" $P break \\\\my\\\\class::method#2" CR
+" $P b \\\\my\\\\class::method#2" CR
+" Break at the opline #2 of the method \\\\my\\\\class::method" CR CR
+
+" $P break test.php:#3" CR
+" $P b test.php:#3" CR
+" Break at opline #3 in test.php" CR CR
+
+" $P break if $cnt > 10" CR
+" $P b if $cnt > 10" CR
+" Break when the condition ($cnt > 10) evaluates to true" CR CR
+
+" $P break at phpdbg::isGreat if $opt == 'S'" CR
+" $P break @ phpdbg::isGreat if $opt == 'S'" CR
+" Break at any opcode in phpdbg::isGreat when the condition ($opt == 'S') is true" CR CR
+
+" $P break at test.php:20 if !isset($x)" CR
+" Break at every opcode on line 20 of test.php when the condition evaluates to true" CR CR
+
+" $P break ZEND_ADD" CR
+" $P b ZEND_ADD" CR
+" Break on any occurence of the opcode ZEND_ADD" CR CR
+
+" $P break del 2" CR
+" $P b ~ 2" CR
+" Remove breakpoint 2" CR CR
+
+"Note: Conditional breaks are costly in terms of runtime overhead. Use them only when required "
+"as they significantly slow execution." CR CR
+
+"Note: An address is only valid for the current compilation."
+},
+
+{"clean",
+"Classes, constants or functions can only be declared once in PHP. You may experience errors "
+"during a debug session if you attempt to recompile a PHP source. The clean command clears "
+"the Zend runtime tables which holds the sets of compiled classes, constants and functions, "
+"releasing any associated storage back into the storage pool. This enables recompilation to "
+"take place." CR CR
+
+"Note that you cannot selectively trim any of these resource pools. You can only do a complete "
+"clean."
+},
+
+{"clear",
+"Clearing breakpoints means you can once again run code without interruption." CR CR
+
+"Note: use break delete N to clear a specific breakpoint." CR CR
+
+"Note: if all breakpoints are cleared, then the PHP script will run until normal completion."
+},
+
+{"ev",
+"The **ev** command takes a string expression which it evaluates and then displays. It "
+"evaluates in the context of the lowest (that is the executing) frame, unless this has first "
+"been explicitly changed by issuing a **frame** command. " CR CR
+
+"**Examples**" CR CR
+" $P ev $variable" CR
+" Will print_r($variable) on the console, if it is defined" CR CR
+
+" $P ev $variable = \"Hello phpdbg :)\"" CR
+" Will set $variable in the current scope" CR CR
+
+"Note that **ev** allows any valid PHP expression including assignments, function calls and "
+"other write statements. This enables you to change the environment during execution, so care "
+"is needed here. You can even call PHP functions which have breakpoints defined. " CR CR
+
+"Note: **ev** will always show the result, so do not prefix the code with **return**"
+},
+
+{"exec",
+"The **exec** command sets the execution context, that is the script to be executed. The "
+"execution context must be defined either by executing the **exec** command or by using the "
+"**-e** command line option." CR CR
+
+"Note that the **exec** command also can be used to replace a previously defined execution "
+"context." CR CR
+
+"**Examples**" CR CR
+
+" $P exec /tmp/script.php" CR
+" $P e /tmp/script.php" CR
+" Set the execution context to **/tmp/script.php**"
+},
+
+//*********** Does F skip any breakpoints lower stack frames or only the current??
+{"finish",
+"The **finish** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered within the current stack frame will be skipped. Execution "
+"will then continue until the next breakpoint after leaving the stack frame or unitil "
+"completion of the script" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current stack frame are also "
+"skipped. "CR CR
+
+"Note **finish** will trigger a \"not executing\" error if not executing."
+},
+
+{"frame",
+"The **frame** takes an optional integer argument. If omitted, then the current frame is displayed "
+"If specified then the current scope is set to the corresponding frame listed in a **back** trace. " "This can be used to allowing access to the variables in a higher stack frame than that currently "
+"being executed." CR CR
+
+"**Examples**" CR CR
+" $P frame 2" CR
+" $P E $count" CR
+" Go to frame 2 and print out variable **$count** in that frame" CR CR
+
+"Note that this frame scope is discarded when execution continues, with the execution frame "
+"then reset to the lowest executiong frame."
+},
+
+{"info",
+"**info** commands provide quick access to various types of information about the PHP environment" CR
+"Specific info commands are show below:" CR CR
+
+" **Target** **Alias** **Purpose**" CR
+" **break** **b** show current breakpoints" CR
+" **files** **F** show included files" CR
+" **classes** **c** show loaded classes" CR
+" **funcs** **f** show loaded classes" CR
+" **error** **e** show last error" CR
+" **vars** **v** show active variables" CR
+" **literal** **l** show active literal constants" CR
+" **memory** **m** show memory manager stats"
+},
+
+// ******** same issue about breakpoints in called frames
+{"leave",
+"The **leave** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered within the current stack frame will be skipped. In effect a "
+"temporary breakpoint is associated with any return opcode, so that a break in execution occurs "
+"before leaving the current stack frame. This allows inspection / modification of any frame "
+"variables including the return value before it is returned" CR CR
+
+"**Examples**" CR CR
+
+" $P leave" CR
+" $P L" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current stack frame are also "
+"skipped. "CR CR
+
+"Note **leave** will trigger a \"not executing\" error if not executing."
+},
+
+{"list",
+"The list command displays source code for the given argument. The target type is specficied by "
+"a second subcommand keyword:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **lines** **l** List N lines from the current execution point" CR
+" **func** **f** List the complete source for a specified function" CR
+" **method** **m** List the complete source for a specified class::method" CR
+" **class** **c** List the complete source for a specified class" CR CR
+
+"Note that the context of **lines**, **func** and **method** can be determined by parsing the "
+"argument, so these subcommands are optional. However, you must specify the **class** keyword "
+"to list off a class." CR CR
+
+"**Examples**" CR CR
+" $P list 2" CR
+" $P l l 2" CR
+" List the next 2 lines from the current file" CR CR
+
+" $P list my_function" CR
+" $P l f my_function" CR
+" List the source of the function **my_function**" CR CR
+
+//************ ????
+" $P list func .mine" CR
+" $P l f .mine" CR
+" List the source of the method **mine** from the active class in scope" CR CR
+
+" $P list m my::method" CR
+" $P l my::method" CR
+" List the source of **my::method**" CR CR
+
+" $P list c myClass" CR
+" $P l c myClass" CR
+" List the source of **myClass**" CR CR
+
+"Note that functions and classes can only be listed if the corresponding classes and functions "
+"table in the Zend executor has a corresponding entry. You can use the compile command to "
+"populate these tables for a given execution context."
+},
+
+{"continue",
+"Continue with execution after hitting a break or watchpoint" CR CR
+
+"**Examples**" CR CR
+" $P continue" CR
+" $P c" CR
+" Continue executing until the next break or watchpoint" CR CR
+
+"Note **continue** will trigger a \"not running\" error if not executing."
+},
+
+{"print",
+"By default, print will show information about the current execution context." CR
+"Other printing commands give access to instruction information." CR
+"Specific printers loaded are show below:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **exec** **e** print out the instructions in the execution context" CR
+" **opline** **o** print out the instruction in the current opline" CR
+" **class** **c** print out the instructions in the specified class" CR
+" **method** **m** print out the instructions in the specified method" CR
+" **func** **f** print out the instructions in the specified function" CR
+" **stack** **s** print out the instructions in the current stack" CR CR
+
+"**Examples**" CR CR
+" $P print class \\\\my\\\\class" CR
+" $P p c \\\\my\\\\class" CR
+" Print the instructions for the methods in \\\\my\\\\class" CR CR
+
+" $P print method \\\\my\\\\class::method" CR
+" $P p m \\\\my\\\\class::method" CR
+" Print the instructions for \\\\my\\\\class::method" CR CR
+
+" $P print func .getSomething" CR
+" $P p f .getSomething" CR
+//************* Check this local method scope
+" Print the instructions for ::getSomething in the active scope" CR CR
+
+" $P print func my_function" CR
+" $P p f my_function" CR
+" Print the instructions for the global function my_function" CR CR
+
+" $P print opline" CR
+" $P p o" CR
+" Print the instruction for the current opline" CR CR
+
+" $P print exec" CR
+" $P p e" CR
+" Print the instructions for the execution context" CR CR
+
+" $P print stack" CR
+" $P p s" CR
+" Print the instructions for the current stack"
+},
+
+{"register",
+//******* Needs a general explanation of the how registered functions work
+"Register any global function for use as a command in phpdbg console" CR CR
+
+"**Examples**" CR CR
+" $P register scandir" CR
+" $P R scandir" CR
+" Will register the scandir function for use in phpdbg" CR CR
+
+"Note: arguments passed as strings, return (if present) print_r'd on console"
+},
+
+{"run",
+"Enter the vm, startinging execution. Execution will then continue until the next breakpoint "
+"or completion of the script. Add parameters you want to use as $argv"
+"**Examples**" CR CR
+" $P run" CR
+" $P r" CR
+" Will cause execution of the context, if it is set" CR CR
+" $P r test" CR
+" Will execute with $argv[1] == \"test\"" CR CR
+
+"Note that the execution context must be set. If not previously compiled, then the script will "
+"be compiled before execution." CR CR
+
+"Note that attempting to run a script that is already executing will result in an \"execution "
+"in progress\" error."
+},
+
+{"set",
+"The **set** command is used to configure how phpdbg looks and behaves. Specific set commands "
+"are as follows:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **prompt** **p** set the prompt" CR
+" **color** **c** set color <element> <color>" CR
+" **colors** **C** set colors [<on|off>]" CR
+" **oplog** **O** set oplog [output]" CR
+" **break** **b** set break **id** <on|off>" CR
+" **breaks** **B** set breaks [<on|off>]" CR
+" **quiet** **q** set quiet [<on|off>]" CR
+" **stepping** **s** set stepping [<opcode|line>]" CR
+" **refcount** **r** set refcount [<on|off>] " CR CR
+
+"Valid colors are **none**, **white**, **red**, **green**, **yellow**, **blue**, **purple**, "
+"**cyan** and **black**. All colours except **none** can be followed by an optional "
+"**-bold** or **-underline** qualifier." CR CR
+
+"Color elements can be one of **prompt**, **notice**, or **error**." CR CR
+
+"**Examples**" CR CR
+" $P S C on" CR
+" Set colors on" CR CR
+
+" $P set p >" CR
+" $P set color prompt white-bold" CR
+" Set the prompt to a bold >" CR CR
+
+" $P S c error red-bold" CR
+" Use red bold for errors" CR CR
+
+" $P S refcount on" CR
+" Enable refcount display when hitting watchpoints" CR CR
+
+" $P S b 4 off" CR
+" Temporarily disable breakpoint 4. This can be subsequently reenabled by a **s b 4 on**." CR
+//*********** check oplog syntax
+},
+
+{"sh",
+"Direct access to shell commands saves having to switch windows/consoles" CR CR
+
+"**Examples**" CR CR
+" $P sh ls /usr/src/php-src" CR
+" Will execute ls /usr/src/php-src, displaying the output in the console"
+//*********** what does this mean????Note: read only commands please!
+},
+
+{"source",
+"Sourcing a **phpdbginit** script during your debugging session might save some time." CR CR
+
+"**Examples**" CR CR
+
+" $P source /my/init" CR
+" $P < /my/init" CR
+" Will execute the phpdbginit file at /my/init" CR CR
+},
+
+{"export",
+"Exporting breakpoints allows you to share, and or save your current debugging session" CR CR
+
+"**Examples**" CR CR
+
+" $P export /my/exports" CR
+" $P > /my/exports" CR
+" Will export all breakpoints to /my/exports" CR CR
+},
+
+{"step",
+"Execute opcodes until next line" CR CR
+
+"**Examples**" CR CR
+
+" $P s" CR
+" Will continue and break again in the next encountered line" CR CR
+},
+
+{"until",
+"The **until** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered before the next source line will be skipped. Execution "
+"will then continue until the next breakpoint or completion of the script" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current line are also skipped. "CR CR
+
+"Note that if the next line is **not** executed then **all** subsequent breakpoints will be "
+"skipped. " CR CR
+
+"Note **until** will trigger a \"not executing\" error if not executing."
+
+},
+{"watch",
+"Sets watchpoints on variables as long as they are defined" CR
+"Passing no parameter to **watch**, lists all actually active watchpoints" CR CR
+
+"**Format for $variable**" CR CR
+" **$var** Variable $var" CR
+" **$var[]** All array elements of $var" CR
+" **$var->** All properties of $var" CR
+" **$var->a** Property $var->a" CR
+" **$var[b]** Array element with key b in array $var" CR CR
+
+"Subcommands of **watch**:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **array** **a** Sets watchpoint on array/object to observe if an entry is added or removed" CR
+" **recursive** **r** Watches variable recursively and automatically adds watchpoints if some entry is added to an array/object" CR
+" **delete** **d** Removes watchpoint" CR CR
+
+"Note when **recursive** watchpoints are removed, watchpoints on all the children are removed too" CR CR
+
+"**Examples**" CR CR
+" $P watch" CR
+" List currently active watchpoints" CR CR
+
+" $P watch $array" CR
+" $P w $array" CR
+" Set watchpoint on $array" CR CR
+
+" $P watch recursive $obj->" CR
+" $P w r $obj->" CR
+" Set recursive watchpoint on $obj->" CR CR
+
+" $P watch delete $obj->a" CR
+" $P w d $obj->a" CR
+" Remove watchpoint $obj->a" CR CR
+
+"Technical note: If using this feature with a debugger, you will get many segmentation faults, each time when a memory page containing a watched address is hit." CR
+" You then you can continue, phpdbg will remove the write protection, so that the program can continue." CR
+" If phpdbg could not handle that segfault, the same segfault is triggered again and this time phpdbg will abort."
+},
+{NULL, NULL /* end of table marker */}
+}; /* }}} */
diff --git a/sapi/phpdbg/phpdbg_help.h b/sapi/phpdbg/phpdbg_help.h
index 319142cb5b..16a1e771e3 100644
--- a/sapi/phpdbg/phpdbg_help.h
+++ b/sapi/phpdbg/phpdbg_help.h
@@ -30,63 +30,19 @@
/**
* Helper Forward Declarations
*/
-PHPDBG_HELP(exec);
-PHPDBG_HELP(compile);
-PHPDBG_HELP(step);
-PHPDBG_HELP(next);
-PHPDBG_HELP(run);
-PHPDBG_HELP(eval);
-PHPDBG_HELP(until);
-PHPDBG_HELP(finish);
-PHPDBG_HELP(leave);
-PHPDBG_HELP(print);
-PHPDBG_HELP(break);
-PHPDBG_HELP(clean);
-PHPDBG_HELP(clear);
-PHPDBG_HELP(info);
-PHPDBG_HELP(back);
-PHPDBG_HELP(frame);
-PHPDBG_HELP(quiet);
-PHPDBG_HELP(list);
-PHPDBG_HELP(set);
-PHPDBG_HELP(register);
-PHPDBG_HELP(options);
-PHPDBG_HELP(source);
-PHPDBG_HELP(shell);
+PHPDBG_HELP(aliases);
-/**
- * Commands
- */
-static const phpdbg_command_t phpdbg_help_commands[] = {
- PHPDBG_COMMAND_D_EX(exec, "the execution context should be a valid path", 'e', help_exec, NULL, 0),
- PHPDBG_COMMAND_D_EX(compile, "allow inspection of code before execution", 'c', help_compile, NULL, 0),
- PHPDBG_COMMAND_D_EX(step, "step through execution to break at every opcode", 's', help_step, NULL, 0),
- PHPDBG_COMMAND_D_EX(next, "continue executing while stepping or after breaking", 'n', help_next, NULL, 0),
- PHPDBG_COMMAND_D_EX(run, "execute inside the phpdbg vm", 'r', help_run, NULL, 0),
- PHPDBG_COMMAND_D_EX(eval, "access to eval() allows affecting the environment", 'E', help_eval, NULL, 0),
- PHPDBG_COMMAND_D_EX(until, "continue until the current line is executed", 'u', help_until, NULL, 0),
- PHPDBG_COMMAND_D_EX(finish, "continue until the current function has returned", 'F', help_finish, NULL, 0),
- PHPDBG_COMMAND_D_EX(leave, "continue until the current function is returning", 'L', help_leave, NULL, 0),
- PHPDBG_COMMAND_D_EX(print, "print context information or instructions", 'p', help_print, NULL, 0),
- PHPDBG_COMMAND_D_EX(break, "breakpoints allow execution interruption", 'b', help_break, NULL, 0),
- PHPDBG_COMMAND_D_EX(clean, "resetting the environment is useful while debugging", 'X', help_clean, NULL, 0),
- PHPDBG_COMMAND_D_EX(clear, "reset breakpoints to execute without interruption", 'c', help_clear, NULL, 0),
- PHPDBG_COMMAND_D_EX(info, "quick access to useful information on the console", 'i', help_info, NULL, 0),
- PHPDBG_COMMAND_D_EX(back, "show debug backtrace information during execution", 't', help_back, NULL, 0),
- PHPDBG_COMMAND_D_EX(frame, "switch to a frame in the current stack for inspection", 'f', help_frame, NULL, 0),
- PHPDBG_COMMAND_D_EX(quiet, "be quiet during execution", 'Q', help_quiet, NULL, 0),
- PHPDBG_COMMAND_D_EX(list, "list code gives you quick access to code", 'l', help_list, NULL, 0),
- PHPDBG_COMMAND_D_EX(set, "configure how phpdbg looks and behaves", 'S', help_set, NULL, 0),
- PHPDBG_COMMAND_D_EX(register, "register a function for use as a command", 'R', help_register,NULL, 0),
- PHPDBG_COMMAND_D_EX(options, "show information about command line options", 'o', help_options, NULL, 0),
- PHPDBG_COMMAND_D_EX(source, "load a phpdbginit file at the console", '.', help_source, NULL, 0),
- PHPDBG_COMMAND_D_EX(shell, "execute system commands with direct shell access", '-', help_shell, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_help_commands[];
#define phpdbg_help_header() \
phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
#define phpdbg_help_footer() \
phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
+typedef struct _phpdbg_help_text_t {
+ char *key;
+ char *text;
+} phpdbg_help_text_t;
+
+extern phpdbg_help_text_t phpdbg_help_text[];
#endif /* PHPDBG_HELP_H */
diff --git a/sapi/phpdbg/phpdbg_info.c b/sapi/phpdbg/phpdbg_info.c
index 0f4233bf30..97f88bfa1e 100644
--- a/sapi/phpdbg/phpdbg_info.c
+++ b/sapi/phpdbg/phpdbg_info.c
@@ -23,9 +23,25 @@
#include "phpdbg_utils.h"
#include "phpdbg_info.h"
#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14])
+
+const phpdbg_command_t phpdbg_info_commands[] = {
+ PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
PHPDBG_INFO(break) /* {{{ */
{
phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC);
diff --git a/sapi/phpdbg/phpdbg_info.h b/sapi/phpdbg/phpdbg_info.h
index a6b4e3719f..c36e6bebd6 100644
--- a/sapi/phpdbg/phpdbg_info.h
+++ b/sapi/phpdbg/phpdbg_info.h
@@ -34,16 +34,6 @@ PHPDBG_INFO(vars);
PHPDBG_INFO(literal);
PHPDBG_INFO(memory);
-static const phpdbg_command_t phpdbg_info_commands[] = {
- PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0),
- PHPDBG_COMMAND_D_EX(files, "show included files", 'F', info_files, NULL, 0),
- PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0),
- PHPDBG_COMMAND_D_EX(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
- PHPDBG_COMMAND_D_EX(error, "show last error", 'e', info_error, NULL, 0),
- PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0),
- PHPDBG_COMMAND_D_EX(literal, "show active literal constants", 'l', info_literal, NULL, 0),
- PHPDBG_COMMAND_D_EX(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_info_commands[];
#endif /* PHPDBG_INFO_H */
diff --git a/sapi/phpdbg/phpdbg_lexer.c b/sapi/phpdbg/phpdbg_lexer.c
new file mode 100644
index 0000000000..c289004b98
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.c
@@ -0,0 +1,2271 @@
+#line 2 "sapi/phpdbg/phpdbg_lexer.c"
+
+#line 4 "sapi/phpdbg/phpdbg_lexer.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 37
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+static void yyensure_buffer_stack (yyscan_t yyscanner );
+static void yy_load_buffer_state (yyscan_t yyscanner );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define yywrap(yyscanner) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 18
+#define YY_END_OF_BUFFER 19
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_acclist[229] =
+ { 0,
+ 19, 4, 15, 18, 4, 17, 18, 17, 18, 4,
+ 7, 18, 4, 12, 15, 18, 4, 12, 15, 18,
+ 4, 9, 18, 4, 15, 18, 4, 15, 18, 4,
+ 15, 18, 4, 15, 18, 4, 15, 18, 4, 15,
+ 18, 3, 4, 15, 18, 4, 15, 18, 4, 15,
+ 18, 4, 15, 18, 4, 15, 18, 16, 18, 16,
+ 17, 18, 15, 18, 7, 18, 12, 15, 18, 12,
+ 15, 18, 9, 18, 15, 18, 15, 18, 15, 18,
+ 15, 18, 15, 18, 15, 18, 15, 18, 15, 18,
+ 15, 18, 15, 18, 4, 15, 4, 4, 4, 17,
+
+ 17, 4, 12, 15, 4, 15, 4, 8, 4, 15,
+ 4, 15, 4, 15, 1, 4, 15, 4, 15, 4,
+ 11, 15, 4, 15, 4, 10, 15, 4, 15, 2,
+ 4, 15, 4, 15, 4, 15, 4, 15, 16, 16,
+ 17, 15, 12, 15, 15, 8, 15, 15, 15, 15,
+ 5, 15, 11, 15, 15, 10, 15, 15, 15, 15,
+ 4, 4, 13, 15, 4, 15, 4, 15, 4, 15,
+ 4, 15, 3, 4, 15, 4, 15, 4, 15, 13,
+ 15, 15, 15, 15, 15, 15, 15, 4, 6, 4,
+ 15, 4, 15, 4, 15, 4, 15, 4, 15, 6,
+
+ 15, 15, 15, 15, 15, 4, 15, 4, 15, 4,
+ 15, 15, 15, 15, 4, 14, 15, 4, 15, 4,
+ 15, 14, 15, 15, 15, 4, 15, 15
+ } ;
+
+static yyconst flex_int16_t yy_accept[127] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 5, 8,
+ 10, 13, 17, 21, 24, 27, 30, 33, 36, 39,
+ 42, 46, 49, 52, 55, 58, 60, 63, 65, 67,
+ 70, 73, 75, 77, 79, 81, 83, 85, 87, 89,
+ 91, 93, 95, 97, 98, 99, 101, 102, 105, 107,
+ 109, 111, 113, 115, 118, 120, 123, 125, 128, 130,
+ 133, 135, 137, 139, 140, 142, 143, 143, 145, 146,
+ 147, 148, 149, 150, 151, 153, 155, 156, 158, 159,
+ 160, 161, 162, 165, 167, 169, 171, 173, 176, 178,
+ 180, 180, 182, 183, 184, 185, 186, 187, 188, 190,
+
+ 192, 194, 196, 198, 200, 201, 202, 203, 204, 205,
+ 206, 208, 210, 212, 213, 214, 215, 218, 220, 222,
+ 224, 225, 226, 228, 229, 229
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 4, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 5, 6, 7, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 9, 1, 1,
+ 1, 1, 1, 1, 10, 10, 10, 11, 12, 10,
+ 13, 13, 13, 13, 13, 13, 13, 14, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 15,
+ 1, 1, 1, 1, 16, 1, 17, 18, 10, 19,
+
+ 20, 21, 13, 22, 23, 13, 13, 24, 13, 25,
+ 26, 13, 13, 27, 28, 29, 30, 31, 13, 32,
+ 33, 34, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[35] =
+ { 0,
+ 1, 2, 3, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[133] =
+ { 0,
+ 0, 0, 33, 35, 38, 0, 357, 71, 74, 76,
+ 352, 79, 87, 90, 96, 100, 108, 109, 112, 120,
+ 123, 126, 138, 142, 148, 0, 104, 316, 455, 151,
+ 162, 313, 73, 92, 65, 146, 122, 94, 152, 127,
+ 155, 163, 172, 318, 131, 176, 182, 288, 190, 314,
+ 178, 184, 187, 211, 212, 215, 221, 224, 225, 235,
+ 236, 239, 245, 0, 203, 260, 259, 248, 264, 455,
+ 237, 190, 243, 253, 255, 222, 259, 194, 249, 250,
+ 277, 229, 0, 285, 286, 289, 295, 304, 307, 310,
+ 184, 0, 252, 283, 292, 296, 306, 309, 163, 328,
+
+ 329, 332, 341, 344, 455, 326, 331, 330, 342, 343,
+ 362, 370, 371, 394, 368, 356, 410, 384, 404, 426,
+ 376, 378, 420, 411, 455, 442, 445, 447, 450, 148,
+ 452, 96
+ } ;
+
+static yyconst flex_int16_t yy_def[133] =
+ { 0,
+ 125, 1, 126, 126, 125, 5, 125, 127, 128, 125,
+ 128, 127, 127, 128, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 129, 129, 130, 125, 130,
+ 130, 125, 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 127, 128, 128, 128, 125, 13, 13, 128,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 129, 129, 130, 125, 130, 130, 125,
+ 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 128, 49, 127, 127, 127, 127, 127, 127, 127,
+ 125, 69, 130, 130, 130, 130, 130, 130, 128, 127,
+
+ 127, 127, 127, 127, 125, 130, 130, 130, 130, 130,
+ 131, 127, 127, 132, 130, 130, 131, 127, 127, 132,
+ 130, 130, 127, 130, 0, 125, 125, 125, 125, 125,
+ 125, 125
+ } ;
+
+static yyconst flex_int16_t yy_nxt[490] =
+ { 0,
+ 8, 9, 10, 11, 12, 8, 13, 12, 14, 8,
+ 8, 8, 8, 8, 15, 8, 8, 8, 16, 17,
+ 18, 8, 8, 8, 19, 20, 21, 22, 23, 8,
+ 8, 8, 24, 25, 27, 10, 27, 10, 28, 10,
+ 10, 29, 30, 28, 31, 30, 32, 28, 28, 28,
+ 28, 28, 33, 28, 28, 28, 34, 35, 36, 28,
+ 37, 28, 38, 39, 28, 28, 40, 28, 28, 28,
+ 41, 42, 44, 67, 44, 46, 47, 47, 47, 45,
+ 44, 67, 44, 48, 71, 48, 48, 45, 44, 73,
+ 44, 48, 125, 48, 48, 45, 120, 44, 50, 44,
+
+ 67, 44, 67, 44, 45, 65, 47, 51, 45, 44,
+ 44, 44, 44, 44, 72, 44, 45, 45, 49, 76,
+ 45, 44, 52, 44, 44, 55, 44, 44, 45, 44,
+ 67, 45, 53, 125, 45, 67, 82, 56, 54, 44,
+ 57, 44, 75, 44, 58, 44, 45, 60, 66, 44,
+ 45, 44, 59, 79, 67, 68, 45, 68, 68, 67,
+ 67, 62, 74, 67, 61, 125, 68, 63, 68, 68,
+ 67, 67, 77, 44, 80, 44, 78, 46, 47, 44,
+ 45, 44, 81, 47, 47, 44, 45, 44, 44, 105,
+ 44, 84, 45, 69, 43, 45, 83, 83, 67, 83,
+
+ 83, 83, 67, 86, 65, 47, 83, 83, 83, 83,
+ 83, 85, 44, 44, 44, 44, 44, 94, 44, 45,
+ 45, 43, 44, 45, 44, 44, 44, 44, 44, 45,
+ 67, 125, 45, 45, 99, 87, 44, 44, 44, 44,
+ 44, 56, 44, 45, 45, 67, 44, 45, 44, 88,
+ 93, 67, 68, 45, 68, 68, 67, 67, 67, 95,
+ 67, 67, 106, 67, 91, 89, 58, 67, 67, 90,
+ 92, 92, 67, 92, 92, 92, 96, 78, 97, 76,
+ 92, 92, 92, 92, 92, 67, 44, 44, 44, 44,
+ 44, 67, 44, 45, 45, 100, 44, 45, 44, 107,
+
+ 67, 98, 101, 45, 67, 44, 102, 44, 44, 108,
+ 44, 44, 45, 44, 67, 45, 125, 67, 45, 43,
+ 125, 70, 103, 109, 67, 78, 58, 110, 104, 44,
+ 44, 44, 44, 44, 67, 44, 45, 45, 67, 67,
+ 45, 114, 44, 111, 44, 44, 112, 44, 115, 45,
+ 67, 67, 45, 116, 125, 113, 125, 125, 114, 111,
+ 56, 76, 43, 44, 67, 44, 43, 43, 43, 43,
+ 45, 44, 44, 44, 44, 122, 67, 43, 45, 45,
+ 125, 125, 125, 125, 67, 44, 67, 44, 125, 125,
+ 119, 121, 45, 118, 66, 124, 78, 125, 66, 66,
+
+ 66, 66, 67, 123, 125, 44, 125, 44, 125, 66,
+ 43, 44, 45, 44, 43, 43, 43, 43, 45, 67,
+ 125, 44, 58, 44, 125, 43, 66, 125, 45, 76,
+ 66, 66, 66, 66, 67, 125, 125, 125, 56, 125,
+ 125, 66, 26, 26, 26, 43, 43, 44, 44, 44,
+ 64, 64, 117, 117, 7, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125
+ } ;
+
+static yyconst flex_int16_t yy_chk[490] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 3, 3, 4, 4, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 8, 35, 8, 9, 9, 10, 10, 8,
+ 12, 33, 12, 12, 33, 12, 12, 12, 13, 35,
+ 13, 13, 14, 13, 13, 13, 132, 15, 14, 15,
+
+ 34, 16, 38, 16, 15, 27, 27, 15, 16, 17,
+ 18, 17, 18, 19, 34, 19, 17, 18, 13, 38,
+ 19, 20, 16, 20, 21, 18, 21, 22, 20, 22,
+ 37, 21, 17, 45, 22, 40, 45, 19, 17, 23,
+ 20, 23, 37, 24, 20, 24, 23, 22, 130, 25,
+ 24, 25, 21, 40, 36, 30, 25, 30, 30, 30,
+ 39, 24, 36, 41, 23, 99, 31, 25, 31, 31,
+ 31, 42, 39, 43, 41, 43, 39, 46, 46, 51,
+ 43, 51, 42, 47, 47, 52, 51, 52, 53, 91,
+ 53, 51, 52, 31, 49, 53, 49, 49, 72, 49,
+
+ 49, 49, 78, 53, 65, 65, 49, 49, 49, 49,
+ 49, 52, 54, 55, 54, 55, 56, 72, 56, 54,
+ 55, 49, 57, 56, 57, 58, 59, 58, 59, 57,
+ 76, 82, 58, 59, 82, 55, 60, 61, 60, 61,
+ 62, 57, 62, 60, 61, 71, 63, 62, 63, 59,
+ 71, 73, 68, 63, 68, 68, 68, 79, 80, 73,
+ 93, 74, 93, 75, 67, 61, 62, 77, 66, 63,
+ 69, 69, 69, 69, 69, 69, 74, 80, 79, 77,
+ 69, 69, 69, 69, 69, 81, 84, 85, 84, 85,
+ 86, 94, 86, 84, 85, 84, 87, 86, 87, 94,
+
+ 95, 81, 85, 87, 96, 88, 86, 88, 89, 95,
+ 89, 90, 88, 90, 97, 89, 50, 98, 90, 48,
+ 44, 32, 87, 96, 28, 97, 89, 98, 90, 100,
+ 101, 100, 101, 102, 106, 102, 100, 101, 108, 107,
+ 102, 106, 103, 100, 103, 104, 101, 104, 107, 103,
+ 109, 110, 104, 108, 11, 102, 7, 0, 110, 104,
+ 103, 109, 111, 111, 116, 111, 111, 111, 111, 111,
+ 111, 112, 113, 112, 113, 116, 115, 111, 112, 113,
+ 0, 0, 0, 0, 121, 118, 122, 118, 0, 0,
+ 113, 115, 118, 112, 114, 121, 122, 0, 114, 114,
+
+ 114, 114, 114, 118, 0, 119, 0, 119, 0, 114,
+ 117, 117, 119, 117, 117, 117, 117, 117, 117, 124,
+ 0, 123, 119, 123, 0, 117, 120, 0, 123, 124,
+ 120, 120, 120, 120, 120, 0, 0, 0, 123, 0,
+ 0, 120, 126, 126, 126, 127, 127, 128, 128, 128,
+ 129, 129, 131, 131, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125
+ } ;
+
+#define REJECT \
+{ \
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ \
+yy_cp = yyg->yy_full_match; /* restore poss. backed-over text */ \
+++yyg->yy_lp; \
+goto find_rule; \
+}
+
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+#line 2 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+/*
+ * phpdbg_lexer.l
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+
+
+
+#define YY_NO_UNISTD_H 1
+#line 629 "sapi/phpdbg/phpdbg_lexer.c"
+
+#define INITIAL 0
+#define RAW 1
+#define NORMAL 2
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ yy_size_t yy_n_chars;
+ yy_size_t yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ yy_state_type *yy_state_buf;
+ yy_state_type *yy_state_ptr;
+ char *yy_full_match;
+ int yy_lp;
+
+ /* These are only needed for trailing context rules,
+ * but there's no conditional variable for that yet. */
+ int yy_looking_for_trail_begin;
+ int yy_full_lp;
+ int *yy_full_state;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+int yyget_column (yyscan_t yyscanner );
+
+void yyset_column (int column_no ,yyscan_t yyscanner );
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner );
+
+void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 44 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+
+#line 882 "sapi/phpdbg/phpdbg_lexer.c"
+
+ yylval = yylval_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ /* Create the reject buffer large enough to save one state per allowed character. */
+ if ( ! yyg->yy_state_buf )
+ yyg->yy_state_buf = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ,yyscanner);
+ if ( ! yyg->yy_state_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yylex()" );
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 126 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ ++yy_cp;
+ }
+ while ( yy_current_state != 125 );
+
+yy_find_action:
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+find_rule: /* we branch to this label when backing up */
+ for ( ; ; ) /* until we find what rule we matched */
+ {
+ if ( yyg->yy_lp && yyg->yy_lp < yy_accept[yy_current_state + 1] )
+ {
+ yy_act = yy_acclist[yyg->yy_lp];
+ {
+ yyg->yy_full_match = yy_cp;
+ break;
+ }
+ }
+ --yy_cp;
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+
+case 1:
+YY_RULE_SETUP
+#line 47 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_EVAL;
+ }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 52 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_SHELL;
+ }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 57 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_RUN;
+ }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 63 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(NORMAL);
+ REJECT;
+ }
+ YY_BREAK
+
+
+case 5:
+YY_RULE_SETUP
+#line 70 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_IF;
+ }
+ YY_BREAK
+
+
+case 6:
+YY_RULE_SETUP
+#line 78 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_PROTO;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 84 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ return T_POUND; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 85 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ return T_DCOLON; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 86 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ return T_COLON; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 88 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 1;
+ return T_TRUTHY;
+ }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 93 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 0;
+ return T_FALSY;
+ }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 98 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = atoi(yytext);
+ return T_DIGITS;
+ }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 103 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, ADDR_PARAM);
+ yylval->addr = strtoul(yytext, 0, 16);
+ return T_ADDR;
+ }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 108 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, OP_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_OPCODE;
+ }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 114 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_ID;
+ }
+ YY_BREAK
+
+case 16:
+YY_RULE_SETUP
+#line 122 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ BEGIN(INITIAL);
+ return T_INPUT;
+}
+ YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+#line 130 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ /* ignore whitespace */ }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1129 "sapi/phpdbg/phpdbg_lexer.c"
+ case YY_STATE_EOF(INITIAL):
+ case YY_STATE_EOF(RAW):
+ case YY_STATE_EOF(NORMAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ yy_size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 126 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+
+ register YY_CHAR yy_c = 1;
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 126 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 125);
+ if ( ! yy_is_jam )
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register yy_size_t number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ yyfree((void *) b ,yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ yy_size_t i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+
+int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+ yyg->yy_state_buf = 0;
+ yyg->yy_state_ptr = 0;
+ yyg->yy_full_match = 0;
+ yyg->yy_lp = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ yyfree ( yyg->yy_state_buf , yyscanner);
+ yyg->yy_state_buf = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+
+
diff --git a/sapi/phpdbg/phpdbg_lexer.h b/sapi/phpdbg/phpdbg_lexer.h
new file mode 100644
index 0000000000..1958cef9a2
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.h
@@ -0,0 +1,348 @@
+#ifndef yyHEADER_H
+#define yyHEADER_H 1
+#define yyIN_HEADER 1
+
+#line 6 "sapi/phpdbg/phpdbg_lexer.h"
+
+#line 8 "sapi/phpdbg/phpdbg_lexer.h"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 37
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+/* Begin user sect3 */
+
+#define yywrap(yyscanner) 1
+#define YY_SKIP_YYWRAP
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define RAW 1
+#define NORMAL 2
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+int yyget_column (yyscan_t yyscanner );
+
+void yyset_column (int column_no ,yyscan_t yyscanner );
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner );
+
+void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+
+#line 347 "sapi/phpdbg/phpdbg_lexer.h"
+#undef yyIN_HEADER
+#endif /* yyHEADER_H */
diff --git a/sapi/phpdbg/phpdbg_lexer.l b/sapi/phpdbg/phpdbg_lexer.l
new file mode 100644
index 0000000000..ad5edd9f8f
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.l
@@ -0,0 +1,131 @@
+%{
+
+/*
+ * phpdbg_lexer.l
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+
+%}
+
+%s RAW
+%s NORMAL
+
+%option outfile="sapi/phpdbg/phpdbg_lexer.c" header-file="sapi/phpdbg/phpdbg_lexer.h"
+%option warn nodefault
+
+%option reentrant noyywrap never-interactive nounistd
+%option bison-bridge
+
+T_TRUE "true"
+T_YES "yes"
+T_ON "on"
+T_ENABLED "enabled"
+T_FALSE "false"
+T_NO "no"
+T_OFF "off"
+T_DISABLED "disabled"
+T_EVAL "ev"
+T_SHELL "sh"
+T_IF "if"
+T_RUN "run"
+T_RUN_SHORT "r"
+
+WS [ \r\n\t]+
+DIGITS [0-9\.]+
+ID [^ \r\n\t:#]+
+ADDR 0x[a-fA-F0-9]+
+OPCODE (ZEND_|zend_)([A-Za-z])+
+INPUT [^\n]+
+%%
+
+<INITIAL>{
+ {T_EVAL} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_EVAL;
+ }
+ {T_SHELL} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_SHELL;
+ }
+ {T_RUN}|{T_RUN_SHORT} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_RUN;
+ }
+
+ .+ {
+ BEGIN(NORMAL);
+ REJECT;
+ }
+}
+
+<NORMAL>{
+ {T_IF} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_IF;
+ }
+}
+
+<INITIAL,NORMAL>{
+ {ID}[:]{1}[//]{2} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_PROTO;
+ }
+ [#]{1} { return T_POUND; }
+ [:]{2} { return T_DCOLON; }
+ [:]{1} { return T_COLON; }
+
+ {T_YES}|{T_ON}|{T_ENABLED}|{T_TRUE} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 1;
+ return T_TRUTHY;
+ }
+ {T_NO}|{T_OFF}|{T_DISABLED}|{T_FALSE} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 0;
+ return T_FALSY;
+ }
+ {DIGITS} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = atoi(yytext);
+ return T_DIGITS;
+ }
+ {ADDR} {
+ phpdbg_init_param(yylval, ADDR_PARAM);
+ yylval->addr = strtoul(yytext, 0, 16);
+ return T_ADDR;
+ }
+ {OPCODE} {
+ phpdbg_init_param(yylval, OP_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_OPCODE;
+ }
+ {ID} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_ID;
+ }
+}
+
+<RAW>{INPUT} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ BEGIN(INITIAL);
+ return T_INPUT;
+}
+
+{WS} { /* ignore whitespace */ }
+%%
diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c
index eb1091550b..037c6c38b2 100644
--- a/sapi/phpdbg/phpdbg_list.c
+++ b/sapi/phpdbg/phpdbg_list.c
@@ -29,9 +29,22 @@
#include "phpdbg.h"
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
+#include "phpdbg_prompt.h"
+#include "php_streams.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13])
+
+const phpdbg_command_t phpdbg_list_commands[] = {
+ PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l"),
+ PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s"),
+ PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m"),
+ PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s"),
+ PHPDBG_END_COMMAND
+};
+
PHPDBG_LIST(lines) /* {{{ */
{
if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) {
@@ -41,12 +54,12 @@ PHPDBG_LIST(lines) /* {{{ */
switch (param->type) {
case NUMERIC_PARAM:
- case EMPTY_PARAM:
phpdbg_list_file(phpdbg_current_file(TSRMLS_C),
- param->type == EMPTY_PARAM ? 0 : (param->num < 0 ? 1 - param->num : param->num),
- (param->type != EMPTY_PARAM && param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
+ (param->num < 0 ? 1 - param->num : param->num),
+ (param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
0 TSRMLS_CC);
break;
+
case FILE_PARAM:
phpdbg_list_file(param->file.name, param->file.line, 0, 0 TSRMLS_CC);
break;
@@ -59,41 +72,28 @@ PHPDBG_LIST(lines) /* {{{ */
PHPDBG_LIST(func) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_list_function_byname(
- param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
+ phpdbg_list_function_byname(param->str, param->len TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_LIST(method) /* {{{ */
{
- switch (param->type) {
- case METHOD_PARAM: {
- zend_class_entry **ce;
-
- if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
- zend_function *function;
- char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
+ zend_class_entry **ce;
- if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
- phpdbg_list_function(function TSRMLS_CC);
- } else {
- phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
- }
+ if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
+ zend_function *function;
+ char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
- efree(lcname);
- } else {
- phpdbg_error("Could not find the class %s", param->method.class);
- }
- } break;
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
+ phpdbg_list_function(function TSRMLS_CC);
+ } else {
+ phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
+ }
- phpdbg_default_switch_case();
+ efree(lcname);
+ } else {
+ phpdbg_error("Could not find the class %s", param->method.class);
}
return SUCCESS;
@@ -101,30 +101,24 @@ PHPDBG_LIST(method) /* {{{ */
PHPDBG_LIST(class) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- zend_class_entry **ce;
-
- if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
- if ((*ce)->type == ZEND_USER_CLASS) {
- if ((*ce)->info.user.filename) {
- phpdbg_list_file(
- (*ce)->info.user.filename,
- (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
- (*ce)->info.user.line_start, 0 TSRMLS_CC
- );
- } else {
- phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
- }
- } else {
- phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
- }
+ zend_class_entry **ce;
+
+ if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ if ((*ce)->type == ZEND_USER_CLASS) {
+ if ((*ce)->info.user.filename) {
+ phpdbg_list_file(
+ (*ce)->info.user.filename,
+ (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
+ (*ce)->info.user.line_start, 0 TSRMLS_CC
+ );
} else {
- phpdbg_error("The requested class (%s) could not be found", param->str);
+ phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
}
- } break;
-
- phpdbg_default_switch_case();
+ } else {
+ phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
+ }
+ } else {
+ phpdbg_error("The requested class (%s) could not be found", param->str);
}
return SUCCESS;
@@ -132,97 +126,46 @@ PHPDBG_LIST(class) /* {{{ */
void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */
{
- unsigned char *mem, *pos, *last_pos, *end_pos;
struct stat st;
-#ifndef _WIN32
- int fd;
-#else
- HANDLE fd, map;
-#endif
- int all_content = (count == 0);
- int line = 0, displayed = 0;
-
+ char *opened = NULL;
+ char buffer[8096] = {0,};
+ long line = 0;
+
+ php_stream *stream = NULL;
+
if (VCWD_STAT(filename, &st) == FAILURE) {
phpdbg_error("Failed to stat file %s", filename);
return;
}
-
-#ifndef _WIN32
- if ((fd = VCWD_OPEN(filename, O_RDONLY)) == FAILURE) {
+
+ stream = php_stream_open_wrapper(filename, "rb", USE_PATH, &opened);
+
+ if (!stream) {
phpdbg_error("Failed to open file %s to list", filename);
return;
}
-
- pos = last_pos = mem = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
- end_pos = mem + st.st_size;
-#else
-
- fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (fd == INVALID_HANDLE_VALUE) {
- phpdbg_error("Failed to open file!");
- return;
- }
-
- map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL);
- if (map == NULL) {
- phpdbg_error("Failed to map file!");
- CloseHandle(fd);
- return;
- }
-
- pos = last_pos = mem = (char*) MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
- if (mem == NULL) {
- phpdbg_error("Failed to map file in memory");
- CloseHandle(map);
- CloseHandle(fd);
- return;
- }
- end_pos = mem + st.st_size;
-#endif
- while (1) {
- if (pos == end_pos) {
- break;
- }
-
- pos = memchr(last_pos, '\n', end_pos - last_pos);
-
- if (!pos) {
- /* No more line breaks */
- pos = end_pos;
- }
-
+
+ while (php_stream_gets(stream, buffer, sizeof(buffer)) != NULL) {
++line;
-
+
if (!offset || offset <= line) {
/* Without offset, or offset reached */
if (!highlight) {
- phpdbg_writeln("%05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ phpdbg_write("%05ld: %s", line, buffer);
} else {
if (highlight != line) {
- phpdbg_writeln(" %05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ phpdbg_write(" %05ld: %s", line, buffer);
} else {
- phpdbg_writeln(">%05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ phpdbg_write(">%05ld: %s", line, buffer);
}
}
- ++displayed;
}
-
- last_pos = pos + 1;
-
- if (!all_content && displayed == count) {
- /* Reached max line to display */
+
+ if ((count + (offset-1)) == line)
break;
- }
}
-
-#ifndef _WIN32
- munmap(mem, st.st_size);
- close(fd);
-#else
- UnmapViewOfFile(mem);
- CloseHandle(map);
- CloseHandle(fd);
-#endif
+
+ php_stream_close(stream);
} /* }}} */
void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
diff --git a/sapi/phpdbg/phpdbg_list.h b/sapi/phpdbg/phpdbg_list.h
index f9d1885eaf..14905f6567 100644
--- a/sapi/phpdbg/phpdbg_list.h
+++ b/sapi/phpdbg/phpdbg_list.h
@@ -36,12 +36,6 @@ void phpdbg_list_function_byname(const char *, size_t TSRMLS_DC);
void phpdbg_list_function(const zend_function* TSRMLS_DC);
void phpdbg_list_file(const char*, long, long, int TSRMLS_DC);
-static const phpdbg_command_t phpdbg_list_commands[] = {
- PHPDBG_COMMAND_D_EX(lines, "lists the specified lines", 'l', list_lines, NULL, 1),
- PHPDBG_COMMAND_D_EX(class, "lists the specified class", 'c', list_class, NULL, 1),
- PHPDBG_COMMAND_D_EX(method, "lists the specified method", 'm', list_method, NULL, 1),
- PHPDBG_COMMAND_D_EX(func, "lists the specified function", 'f', list_func, NULL, 1),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_list_commands[];
#endif /* PHPDBG_LIST_H */
diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c
index 50073eb22b..6b13625fc1 100644
--- a/sapi/phpdbg/phpdbg_opcode.c
+++ b/sapi/phpdbg/phpdbg_opcode.c
@@ -183,6 +183,7 @@ void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags
const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
{
+#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO
#define CASE(s) case s: return #s
switch (opcode) {
CASE(ZEND_NOP);
@@ -360,4 +361,8 @@ const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
default:
return "UNKNOWN";
}
+#else
+ const char *ret = zend_get_opcode_name(opcode);
+ return ret?ret:"UNKNOWN";
+#endif
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_parser.y b/sapi/phpdbg/phpdbg_parser.y
new file mode 100644
index 0000000000..4a84504e2e
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.y
@@ -0,0 +1,168 @@
+%error-verbose
+%{
+
+/*
+ * phpdbg_parser.y
+ * (from php-src root)
+ * flex sapi/phpdbg/dev/phpdbg_lexer.l
+ * bison sapi/phpdbg/dev/phpdbg_parser.y
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_prompt.h"
+
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+#include "phpdbg_lexer.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+int yyerror(phpdbg_param_t *stack, yyscan_t scanner, const char *msg) {
+ TSRMLS_FETCH();
+ phpdbg_error("Parse Error: %s", msg);
+ {
+ const phpdbg_param_t *top = stack;
+
+ while (top) {
+ phpdbg_param_debug(
+ top, "--> ");
+ top = top->next;
+ }
+ }
+ return 0;
+}
+%}
+
+%code requires {
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+}
+%output "sapi/phpdbg/phpdbg_parser.c"
+%defines "sapi/phpdbg/phpdbg_parser.h"
+
+%define api.pure
+%lex-param { yyscan_t scanner }
+%parse-param { phpdbg_param_t *stack }
+%parse-param { yyscan_t scanner }
+
+%token T_EVAL "eval"
+%token T_RUN "run"
+%token T_SHELL "shell"
+%token T_IF "if (condition)"
+%token T_TRUTHY "truthy (true, on, yes or enabled)"
+%token T_FALSY "falsy (false, off, no or disabled)"
+%token T_STRING "string (some input, perhaps)"
+%token T_COLON ": (colon)"
+%token T_DCOLON ":: (double colon)"
+%token T_POUND "# (pound sign)"
+%token T_PROTO "protocol (file://)"
+%token T_DIGITS "digits (numbers)"
+%token T_LITERAL "literal (string)"
+%token T_ADDR "address"
+%token T_OPCODE "opcode"
+%token T_ID "identifier (command or function name)"
+%token T_INPUT "input (input string or data)"
+%token T_UNEXPECTED "input"
+%%
+
+input
+ : parameters
+ | /* nothing */
+ ;
+
+parameters
+ : parameter { phpdbg_stack_push(stack, &$1); }
+ | parameters parameter { phpdbg_stack_push(stack, &$2); }
+ ;
+
+parameter
+ : T_ID T_COLON T_DIGITS {
+ $$.type = FILE_PARAM;
+ $$.file.name = $2.str;
+ $$.file.line = $3.num;
+ }
+ | T_ID T_COLON T_POUND T_DIGITS {
+ $$.type = NUMERIC_FILE_PARAM;
+ $$.file.name = $1.str;
+ $$.file.line = $4.num;
+ }
+ | T_PROTO T_ID T_COLON T_DIGITS {
+ $$.type = FILE_PARAM;
+ $$.file.name = malloc($1.len +
+ $2.len + 1);
+ if ($$.file.name) {
+ memcpy(&$$.file.name[0], $1.str, $1.len);
+ memcpy(&$$.file.name[$1.len], $2.str, $2.len);
+ $$.file.name[$1.len + $2.len] = '\0';
+ }
+ $$.file.line = $4.num;
+ }
+ | T_PROTO T_ID T_COLON T_POUND T_DIGITS {
+ $$.type = NUMERIC_FILE_PARAM;
+ $$.file.name = malloc($1.len +
+ $2.len + 1);
+ if ($$.file.name) {
+ memcpy(&$$.file.name[0], $1.str, $1.len);
+ memcpy(&$$.file.name[$1.len], $2.str, $2.len);
+ $$.file.name[$1.len + $2.len] = '\0';
+ }
+ $$.file.line = $5.num;
+ }
+ | T_ID T_DCOLON T_ID {
+ $$.type = METHOD_PARAM;
+ $$.method.class = $1.str;
+ $$.method.name = $3.str;
+ }
+ | T_ID T_DCOLON T_ID T_POUND T_DIGITS {
+ $$.type = NUMERIC_METHOD_PARAM;
+ $$.method.class = $1.str;
+ $$.method.name = $3.str;
+ $$.num = $5.num;
+ }
+ | T_ID T_POUND T_DIGITS {
+ $$.type = NUMERIC_FUNCTION_PARAM;
+ $$.str = $1.str;
+ $$.len = $1.len;
+ $$.num = $3.num;
+ }
+ | T_IF T_INPUT {
+ $$.type = COND_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_EVAL T_INPUT {
+ $$.type = EVAL_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_SHELL T_INPUT {
+ $$.type = SHELL_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_RUN {
+ $$.type = RUN_PARAM;
+ $$.len = 0;
+ }
+ | T_RUN T_INPUT {
+ $$.type = RUN_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_OPCODE { $$ = $1; }
+ | T_ADDR { $$ = $1; }
+ | T_LITERAL { $$ = $1; }
+ | T_TRUTHY { $$ = $1; }
+ | T_FALSY { $$ = $1; }
+ | T_DIGITS { $$ = $1; }
+ | T_ID { $$ = $1; }
+ ;
+
+%%
diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c
index c4925fe5fd..76321a5042 100644
--- a/sapi/phpdbg/phpdbg_print.c
+++ b/sapi/phpdbg/phpdbg_print.c
@@ -26,6 +26,19 @@
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9])
+
+const phpdbg_command_t phpdbg_print_commands[] = {
+ PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
+ PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
+ PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s"),
+ PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m"),
+ PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s"),
+ PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
PHPDBG_PRINT(opline) /* {{{ */
{
if (EG(in_execution) && EG(current_execute_data)) {
@@ -77,7 +90,7 @@ static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC)
phpdbg_error("\tFailed to decode opline %16p", opline);
}
opline++;
- } while (++opcode < end);
+ } while (opcode++ < end);
zend_hash_destroy(&vars);
}
} break;
@@ -141,36 +154,30 @@ PHPDBG_PRINT(class) /* {{{ */
{
zend_class_entry **ce;
- switch (param->type) {
- case STR_PARAM: {
- if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
- phpdbg_notice("%s %s: %s",
- ((*ce)->type == ZEND_USER_CLASS) ?
- "User" : "Internal",
- ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
- "Interface" :
- ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
- "Abstract Class" :
- "Class",
- (*ce)->name);
-
- phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
- if (zend_hash_num_elements(&(*ce)->function_table)) {
- HashPosition position;
- zend_function *method;
-
- for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
- zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
- zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
- phpdbg_print_function_helper(method TSRMLS_CC);
- }
- }
- } else {
- phpdbg_error("The class %s could not be found", param->str);
+ if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ phpdbg_notice("%s %s: %s",
+ ((*ce)->type == ZEND_USER_CLASS) ?
+ "User" : "Internal",
+ ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
+ "Interface" :
+ ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
+ "Abstract Class" :
+ "Class",
+ (*ce)->name);
+
+ phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
+ if (zend_hash_num_elements(&(*ce)->function_table)) {
+ HashPosition position;
+ zend_function *method;
+
+ for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
+ zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
+ phpdbg_print_function_helper(method TSRMLS_CC);
}
- } break;
-
- phpdbg_default_switch_case();
+ }
+ } else {
+ phpdbg_error("The class %s could not be found", param->str);
}
return SUCCESS;
@@ -178,31 +185,25 @@ PHPDBG_PRINT(class) /* {{{ */
PHPDBG_PRINT(method) /* {{{ */
{
- switch (param->type) {
- case METHOD_PARAM: {
- zend_class_entry **ce;
-
- if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
- zend_function *fbc;
- char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
+ zend_class_entry **ce;
- if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
- phpdbg_notice("%s Method %s",
- (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
- fbc->common.function_name);
+ if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
+ zend_function *fbc;
+ char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
- phpdbg_print_function_helper(fbc TSRMLS_CC);
- } else {
- phpdbg_error("The method %s could not be found", param->method.name);
- }
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("%s Method %s",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ fbc->common.function_name);
- efree(lcname);
- } else {
- phpdbg_error("The class %s could not be found", param->method.class);
- }
- } break;
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("The method %s could not be found", param->method.name);
+ }
- phpdbg_default_switch_case();
+ efree(lcname);
+ } else {
+ phpdbg_error("The class %s could not be found", param->method.class);
}
return SUCCESS;
@@ -210,49 +211,43 @@ PHPDBG_PRINT(method) /* {{{ */
PHPDBG_PRINT(func) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- HashTable *func_table = EG(function_table);
- zend_function* fbc;
- const char *func_name = param->str;
- size_t func_name_len = param->len;
- char *lcname;
- /* search active scope if begins with period */
- if (func_name[0] == '.') {
- if (EG(scope)) {
- func_name++;
- func_name_len--;
-
- func_table = &EG(scope)->function_table;
- } else {
- phpdbg_error("No active class");
- return SUCCESS;
- }
- } else if (!EG(function_table)) {
- phpdbg_error("No function table loaded");
- return SUCCESS;
- } else {
- func_table = EG(function_table);
- }
-
- lcname = zend_str_tolower_dup(func_name, func_name_len);
-
- if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
- phpdbg_notice("%s %s %s",
- (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
- (fbc->common.scope) ? "Method" : "Function",
- fbc->common.function_name);
+ HashTable *func_table = EG(function_table);
+ zend_function* fbc;
+ const char *func_name = param->str;
+ size_t func_name_len = param->len;
+ char *lcname;
+ /* search active scope if begins with period */
+ if (func_name[0] == '.') {
+ if (EG(scope)) {
+ func_name++;
+ func_name_len--;
+
+ func_table = &EG(scope)->function_table;
+ } else {
+ phpdbg_error("No active class");
+ return SUCCESS;
+ }
+ } else if (!EG(function_table)) {
+ phpdbg_error("No function table loaded");
+ return SUCCESS;
+ } else {
+ func_table = EG(function_table);
+ }
- phpdbg_print_function_helper(fbc TSRMLS_CC);
- } else {
- phpdbg_error("The function %s could not be found", func_name);
- }
+ lcname = zend_str_tolower_dup(func_name, func_name_len);
- efree(lcname);
- } break;
+ if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("%s %s %s",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ (fbc->common.scope) ? "Method" : "Function",
+ fbc->common.function_name);
- phpdbg_default_switch_case();
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("The function %s could not be found", func_name);
}
+ efree(lcname);
+
return SUCCESS;
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_print.h b/sapi/phpdbg/phpdbg_print.h
index 80010d5e7c..ed85e965c1 100644
--- a/sapi/phpdbg/phpdbg_print.h
+++ b/sapi/phpdbg/phpdbg_print.h
@@ -35,17 +35,6 @@ PHPDBG_PRINT(method);
PHPDBG_PRINT(func);
PHPDBG_PRINT(stack);
-/**
- * Commands
- */
-static const phpdbg_command_t phpdbg_print_commands[] = {
- PHPDBG_COMMAND_D_EX(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
- PHPDBG_COMMAND_D_EX(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
- PHPDBG_COMMAND_D_EX(class, "print out the instructions in the specified class", 'c', print_class, NULL, 1),
- PHPDBG_COMMAND_D_EX(method, "print out the instructions in the specified method", 'm', print_method, NULL, 1),
- PHPDBG_COMMAND_D_EX(func, "print out the instructions in the specified function", 'f', print_func, NULL, 1),
- PHPDBG_COMMAND_D_EX(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_print_commands[];
#endif /* PHPDBG_PRINT_H */
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index f586bb5a84..7a28a75129 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -35,99 +35,154 @@
#include "phpdbg_cmd.h"
#include "phpdbg_set.h"
#include "phpdbg_frame.h"
+#include "phpdbg_lexer.h"
+#include "phpdbg_parser.h"
+
+int yyparse(phpdbg_param_t *stack, yyscan_t scanner);
/* {{{ command declarations */
const phpdbg_command_t phpdbg_prompt_commands[] = {
- PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1),
- PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0),
- PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1),
- PHPDBG_COMMAND_D(next, "continue execution", 'n', NULL, 0),
- PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0),
- PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1),
+ PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s"),
+ PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0),
+ PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0),
+ PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s"),
+ PHPDBG_COMMAND_D(ev, "evaluate some code", 0, NULL, "i"),
PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0),
PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0),
PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0),
- PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2),
- PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1),
- PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0),
- PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1),
- PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, 2),
- PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, 1),
+ PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0),
+ PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c"),
+ PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n"),
+ PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n"),
+ PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*"),
+ PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s"),
PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0),
PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0),
- PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, 2),
- PHPDBG_COMMAND_D(quiet, "silence some output", 'Q', NULL, 1),
- PHPDBG_COMMAND_D(aliases, "show alias list", 'a', NULL, 0),
- PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, 1),
- PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, 1),
- PHPDBG_COMMAND_D(source, "execute a phpdbginit", '.', NULL, 1),
- PHPDBG_COMMAND_D(shell, "shell a command", '-', NULL, 1),
+ PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s"),
+ PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s"),
+ PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s"),
+ PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s"),
+ PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s"),
+ PHPDBG_COMMAND_D(sh, "shell a command", 0, NULL, "i"),
PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0),
+ PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss"),
PHPDBG_END_COMMAND
}; /* }}} */
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+static inline int phpdbg_call_register(phpdbg_param_t *stack TSRMLS_DC) /* {{{ */
{
- phpdbg_input_t *function = input->argv[0];
-
- if (zend_hash_exists(
- &PHPDBG_G(registered), function->string, function->length+1)) {
-
- zval fname, *fretval;
- zend_fcall_info fci;
-
- ZVAL_STRINGL(&fname, function->string, function->length, 1);
-
- memset(&fci, 0, sizeof(zend_fcall_info));
-
- fci.size = sizeof(zend_fcall_info);
- fci.function_table = &PHPDBG_G(registered);
- fci.function_name = &fname;
- fci.symbol_table = EG(active_symbol_table);
- fci.object_ptr = NULL;
- fci.retval_ptr_ptr = &fretval;
- fci.no_separation = 1;
-
- if (input->argc > 1) {
- int param;
- zval params;
-
- array_init(&params);
+ phpdbg_param_t *name = NULL;
- for (param = 0; param < (input->argc-1); param++) {
- add_next_index_stringl(
- &params,
- input->argv[param+1]->string,
- input->argv[param+1]->length, 1);
+ if (stack->type == STACK_PARAM) {
+ name = stack->next;
+
+ if (!name || name->type != STR_PARAM) {
+ return FAILURE;
+ }
+
+ if (zend_hash_exists(
+ &PHPDBG_G(registered), name->str, name->len+1)) {
+
+ zval fname, *fretval;
+ zend_fcall_info fci;
+
+ ZVAL_STRINGL(&fname, name->str, name->len, 1);
+
+ memset(&fci, 0, sizeof(zend_fcall_info));
+
+ fci.size = sizeof(zend_fcall_info);
+ fci.function_table = &PHPDBG_G(registered);
+ fci.function_name = &fname;
+ fci.symbol_table = EG(active_symbol_table);
+ fci.object_ptr = NULL;
+ fci.retval_ptr_ptr = &fretval;
+ fci.no_separation = 1;
+
+ if (name->next) {
+ zval params;
+ phpdbg_param_t *next = name->next;
+
+ array_init(&params);
+
+ while (next) {
+ char *buffered = NULL;
+
+ switch (next->type) {
+ case OP_PARAM:
+ case COND_PARAM:
+ case STR_PARAM:
+ add_next_index_stringl(
+ &params,
+ next->str,
+ next->len, 1);
+ break;
+
+ case NUMERIC_PARAM:
+ add_next_index_long(&params, next->num);
+ break;
+
+ case METHOD_PARAM:
+ spprintf(&buffered, 0, "%s::%s",
+ next->method.class, next->method.name);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ spprintf(&buffered, 0, "%s::%s#%ld",
+ next->method.class, next->method.name, next->num);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ spprintf(&buffered, 0, "%s#%ld",
+ next->str, next->num);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case FILE_PARAM:
+ spprintf(&buffered, 0, "%s:%ld",
+ next->file.name, next->file.line);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ spprintf(&buffered, 0, "%s:#%ld",
+ next->file.name, next->file.line);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ default: {
+ /* not yet */
+ }
+ }
+
+ next = next->next;
+ }
- phpdbg_debug(
- "created param[%d] from argv[%d]: %s",
- param, param+1, input->argv[param+1]->string);
+ zend_fcall_info_args(&fci, &params TSRMLS_CC);
+ } else {
+ fci.params = NULL;
+ fci.param_count = 0;
}
- zend_fcall_info_args(&fci, &params TSRMLS_CC);
- } else {
- fci.params = NULL;
- fci.param_count = 0;
- }
-
- phpdbg_debug(
- "created %d params from %d arguments",
- fci.param_count, input->argc);
+ phpdbg_debug(
+ "created %d params from arguments",
+ fci.param_count);
- zend_call_function(&fci, NULL TSRMLS_CC);
+ zend_call_function(&fci, NULL TSRMLS_CC);
- if (fretval) {
- zend_print_zval_r(
- fretval, 0 TSRMLS_CC);
- phpdbg_writeln(EMPTY);
- }
+ if (fretval) {
+ zend_print_zval_r(
+ fretval, 0 TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ }
- zval_dtor(&fname);
+ zval_dtor(&fname);
- return SUCCESS;
+ return SUCCESS;
+ }
}
return FAILURE;
@@ -136,7 +191,7 @@ static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ *
void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC) /* {{{ */
{
struct stat sb;
-
+
if (init_file && VCWD_STAT(init_file, &sb) != -1) {
FILE *fp = fopen(init_file, "r");
if (fp) {
@@ -190,19 +245,47 @@ void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_
}
{
- phpdbg_input_t *input = phpdbg_read_input(cmd TSRMLS_CC);
- switch (phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
- case FAILURE:
- if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
- if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) {
- phpdbg_error("Unrecognized command in %s:%d: %s!", init_file, line, input->string);
- }
- }
+ char *why = NULL;
+ char *input = phpdbg_read_input(cmd TSRMLS_CC);
+ phpdbg_param_t stack;
+ yyscan_t scanner;
+ YY_BUFFER_STATE state;
+
+ phpdbg_init_param(&stack, STACK_PARAM);
+
+ if (yylex_init(&scanner)) {
+ phpdbg_error(
+ "could not initialize scanner");
break;
}
+
+ state = yy_scan_string(input, scanner);
+
+ if (yyparse(&stack, scanner) <= 0) {
+ switch (phpdbg_stack_execute(&stack, &why TSRMLS_CC)) {
+ case FAILURE:
+// if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
+ phpdbg_error(
+ "Unrecognized command in %s:%d: %s, %s!",
+ init_file, line, input, why);
+ }
+// }
+ break;
+ }
+ }
+
+ if (why) {
+ free(why);
+ why = NULL;
+ }
+
+ yy_delete_buffer(state, scanner);
+ yylex_destroy(scanner);
+
+ phpdbg_stack_free(&stack);
phpdbg_destroy_input(&input TSRMLS_CC);
}
-
}
next_line:
line++;
@@ -264,47 +347,47 @@ void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TS
PHPDBG_COMMAND(exec) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- struct stat sb;
-
- if (VCWD_STAT(param->str, &sb) != FAILURE) {
- if (sb.st_mode & (S_IFREG|S_IFLNK)) {
- char *res = phpdbg_resolve_path(param->str TSRMLS_CC);
- size_t res_len = strlen(res);
-
- if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) {
-
- if (PHPDBG_G(exec)) {
- phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec));
- efree(PHPDBG_G(exec));
- PHPDBG_G(exec) = NULL;
- PHPDBG_G(exec_len) = 0L;
- }
+ struct stat sb;
- if (PHPDBG_G(ops)) {
- phpdbg_notice("Destroying compiled opcodes");
- phpdbg_clean(0 TSRMLS_CC);
- }
+ if (VCWD_STAT(param->str, &sb) != FAILURE) {
+ if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ char *res = phpdbg_resolve_path(param->str TSRMLS_CC);
+ size_t res_len = strlen(res);
- PHPDBG_G(exec) = res;
- PHPDBG_G(exec_len) = res_len;
+ if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) {
- phpdbg_notice("Set execution context: %s", PHPDBG_G(exec));
- } else {
- phpdbg_notice("Execution context not changed");
- }
- } else {
- phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str);
+ if (PHPDBG_G(exec)) {
+ phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec));
+ efree(PHPDBG_G(exec));
+ PHPDBG_G(exec) = NULL;
+ PHPDBG_G(exec_len) = 0L;
+ }
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_notice("Destroying compiled opcodes");
+ phpdbg_clean(0 TSRMLS_CC);
+ }
+
+ PHPDBG_G(exec) = res;
+ PHPDBG_G(exec_len) = res_len;
+
+ *SG(request_info).argv = PHPDBG_G(exec);
+ php_hash_environment(TSRMLS_C);
+
+ phpdbg_notice("Set execution context: %s", PHPDBG_G(exec));
+
+ if (phpdbg_compile(TSRMLS_C) == FAILURE) {
+ phpdbg_error("Failed to compile %s", PHPDBG_G(exec));
}
} else {
- phpdbg_error("Cannot stat %s, ensure the file exists", param->str);
+ phpdbg_notice("Execution context not changed");
}
- } break;
-
- phpdbg_default_switch_case();
+ } else {
+ phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str);
+ }
+ } else {
+ phpdbg_error("Cannot stat %s, ensure the file exists", param->str);
}
-
return SUCCESS;
} /* }}} */
@@ -312,6 +395,11 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */
{
zend_file_handle fh;
+ if (!PHPDBG_G(exec)) {
+ phpdbg_error("No execution context");
+ return SUCCESS;
+ }
+
if (EG(in_execution)) {
phpdbg_error("Cannot compile while in execution");
return FAILURE;
@@ -334,47 +422,16 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */
return FAILURE;
} /* }}} */
-PHPDBG_COMMAND(compile) /* {{{ */
-{
- if (!PHPDBG_G(exec)) {
- phpdbg_error("No execution context");
- return SUCCESS;
- }
-
- if (!EG(in_execution)) {
- if (PHPDBG_G(ops)) {
- phpdbg_error("Destroying previously compiled opcodes");
- phpdbg_clean(0 TSRMLS_CC);
- }
- }
-
- phpdbg_compile(TSRMLS_C);
-
- return SUCCESS;
-} /* }}} */
-
PHPDBG_COMMAND(step) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM:
- case NUMERIC_PARAM: {
- if (param->type == NUMERIC_PARAM && param->num) {
- PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
- } else {
- PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
- }
-
- phpdbg_notice("Stepping %s",
- (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
- } break;
-
- phpdbg_default_switch_case();
+ if (EG(in_execution)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
}
- return SUCCESS;
+ return PHPDBG_NEXT;
} /* }}} */
-PHPDBG_COMMAND(next) /* {{{ */
+PHPDBG_COMMAND(continue) /* {{{ */
{
return PHPDBG_NEXT;
} /* }}} */
@@ -475,17 +532,9 @@ PHPDBG_COMMAND(leave) /* {{{ */
PHPDBG_COMMAND(frame) /* {{{ */
{
- switch (param->type) {
- case NUMERIC_PARAM:
- phpdbg_switch_frame(param->num TSRMLS_CC);
- break;
-
- case EMPTY_PARAM:
- phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num);
- break;
-
- phpdbg_default_switch_case();
- }
+ if (!param) {
+ phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num);
+ } else phpdbg_switch_frame(param->num TSRMLS_CC);
return SUCCESS;
} /* }}} */
@@ -580,6 +629,31 @@ PHPDBG_COMMAND(run) /* {{{ */
/* reset hit counters */
phpdbg_reset_breakpoints(TSRMLS_C);
+ if (param && param->type != EMPTY_PARAM && param->len != 0) {
+ char **argv = emalloc(5 * sizeof(char *));
+ int argc = 0;
+ int i;
+ char *argv_str = strtok(param->str, " ");
+
+ while (argv_str) {
+ if (argc >= 4 && argc == (argc & -argc)) {
+ argv = erealloc(argv, (argc * 2 + 1) * sizeof(char *));
+ }
+ argv[++argc] = argv_str;
+ argv_str = strtok(0, " ");
+ argv[argc] = estrdup(argv[argc]);
+ }
+ argv[0] = SG(request_info).argv[0];
+ for (i = SG(request_info).argc; --i;) {
+ efree(SG(request_info).argv[i]);
+ }
+ efree(SG(request_info).argv);
+ SG(request_info).argv = erealloc(argv, ++argc * sizeof(char *));
+ SG(request_info).argc = argc;
+
+ php_hash_environment(TSRMLS_C);
+ }
+
zend_try {
php_output_activate(TSRMLS_C);
PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
@@ -615,42 +689,36 @@ out:
return SUCCESS;
} /* }}} */
-PHPDBG_COMMAND(eval) /* {{{ */
+PHPDBG_COMMAND(ev) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING);
- zval retval;
-
- if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
- PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING;
- }
-
- /* disable stepping while eval() in progress */
- PHPDBG_G(flags) |= PHPDBG_IN_EVAL;
- zend_try {
- if (zend_eval_stringl(param->str, param->len,
- &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) {
- zend_print_zval_r(
- &retval, 0 TSRMLS_CC);
- phpdbg_writeln(EMPTY);
- zval_dtor(&retval);
- }
- } zend_end_try();
- PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL;
+ zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING);
+ zval retval;
- /* switch stepping back on */
- if (stepping &&
- !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
- PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
- }
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING;
+ }
- CG(unclean_shutdown) = 0;
- } break;
+ /* disable stepping while eval() in progress */
+ PHPDBG_G(flags) |= PHPDBG_IN_EVAL;
+ zend_try {
+ if (zend_eval_stringl(param->str, param->len,
+ &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) {
+ zend_print_zval_r(
+ &retval, 0 TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ zval_dtor(&retval);
+ }
+ } zend_end_try();
+ PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL;
- phpdbg_default_switch_case();
+ /* switch stepping back on */
+ if (stepping &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
}
+ CG(unclean_shutdown) = 0;
+
return SUCCESS;
} /* }}} */
@@ -661,14 +729,10 @@ PHPDBG_COMMAND(back) /* {{{ */
return SUCCESS;
}
- switch (param->type) {
- case EMPTY_PARAM:
- case NUMERIC_PARAM:
- phpdbg_dump_backtrace(
- (param->type == NUMERIC_PARAM) ? param->num : 0 TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
+ if (!param) {
+ phpdbg_dump_backtrace(0 TSRMLS_CC);
+ } else {
+ phpdbg_dump_backtrace(param->num TSRMLS_CC);
}
return SUCCESS;
@@ -676,47 +740,41 @@ PHPDBG_COMMAND(back) /* {{{ */
PHPDBG_COMMAND(print) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM: {
- phpdbg_writeln(SEPARATE);
- phpdbg_notice("Execution Context Information");
+ phpdbg_writeln(SEPARATE);
+ phpdbg_notice("Execution Context Information");
#ifdef HAVE_LIBREADLINE
- phpdbg_writeln("Readline\tyes");
+ phpdbg_writeln("Readline\tyes");
#else
- phpdbg_writeln("Readline\tno");
+ phpdbg_writeln("Readline\tno");
#endif
- phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none");
- phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no");
- phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
- phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off");
- phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off");
+ phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none");
+ phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no");
+ phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
+ phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off");
+ phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off");
- if (PHPDBG_G(ops)) {
- phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last);
-
- if (PHPDBG_G(ops)->last_var) {
- phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1);
- } else {
- phpdbg_writeln("Variables\tNone");
- }
- }
+ if (PHPDBG_G(ops)) {
+ phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last);
- phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no");
- if (EG(in_execution)) {
- phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret));
- }
+ if (PHPDBG_G(ops)->last_var) {
+ phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1);
+ } else {
+ phpdbg_writeln("Variables\tNone");
+ }
+ }
- phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table)));
- phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
- phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
- phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
+ phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no");
+ if (EG(in_execution)) {
+ phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret));
+ }
- phpdbg_writeln(SEPARATE);
- } break;
+ phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
- phpdbg_default_switch_case();
- }
+ phpdbg_writeln(SEPARATE);
return SUCCESS;
} /* }}} */
@@ -732,19 +790,18 @@ PHPDBG_COMMAND(info) /* {{{ */
PHPDBG_COMMAND(set) /* {{{ */
{
phpdbg_error(
- "No information command selected!");
+ "No set command selected!");
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(break) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_set_breakpoint_file(
+ if (!param) {
+ phpdbg_set_breakpoint_file(
zend_get_executed_filename(TSRMLS_C),
zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
- break;
+ } else switch (param->type) {
case ADDR_PARAM:
phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
break;
@@ -767,9 +824,18 @@ PHPDBG_COMMAND(break) /* {{{ */
case FILE_PARAM:
phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
break;
+ case NUMERIC_FILE_PARAM:
+ phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
+ break;
+ case COND_PARAM:
+ phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
+ break;
case STR_PARAM:
phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
break;
+ case OP_PARAM:
+ phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
+ break;
phpdbg_default_switch_case();
}
@@ -777,83 +843,72 @@ PHPDBG_COMMAND(break) /* {{{ */
return SUCCESS;
} /* }}} */
-PHPDBG_COMMAND(shell) /* {{{ */
+PHPDBG_COMMAND(sh) /* {{{ */
{
- /* don't allow this to loop, ever ... */
- switch (param->type) {
- case STR_PARAM: {
- FILE *fd = NULL;
- if ((fd=VCWD_POPEN((char*)param->str, "w"))) {
- /* do something perhaps ?? do we want input ?? */
- fclose(fd);
- } else {
- phpdbg_error(
- "Failed to execute %s", param->str);
- }
- } break;
-
- phpdbg_default_switch_case();
+ FILE *fd = NULL;
+ if ((fd=VCWD_POPEN((char*)param->str, "w"))) {
+ /* do something perhaps ?? do we want input ?? */
+ fclose(fd);
+ } else {
+ phpdbg_error(
+ "Failed to execute %s", param->str);
}
+
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(source) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- if (input->argc > 2) {
- if (phpdbg_argv_is(1, "export")) {
- FILE *h = VCWD_FOPEN(input->argv[2]->string, "w+");
- if (h) {
- phpdbg_export_breakpoints(h TSRMLS_CC);
- fclose(h);
- } else phpdbg_error("Failed to open %s", input->argv[1]->string);
- } else {
- phpdbg_error(
- "Incorrect usage of source command, see help");
- }
- } else {
- struct stat sb;
- if (VCWD_STAT(param->str, &sb) != -1) {
- phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC);
- } else phpdbg_error("Cannot stat %s", param->str);
- }
- } break;
+ struct stat sb;
+
+ if (VCWD_STAT(param->str, &sb) != -1) {
+ phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC);
+ } else {
+ phpdbg_error(
+ "Failed to stat %s, file does not exist", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
- phpdbg_default_switch_case();
+PHPDBG_COMMAND(export) /* {{{ */
+{
+ FILE *handle = VCWD_FOPEN(param->str, "w+");
+
+ if (handle) {
+ phpdbg_export_breakpoints(handle TSRMLS_CC);
+ fclose(handle);
+ } else {
+ phpdbg_error(
+ "Failed to open or create %s, check path and permissions", param->str);
}
+
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(register) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- zend_function *function;
- char *lcname = zend_str_tolower_dup(param->str, param->len);
- size_t lcname_len = strlen(lcname);
-
- if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) {
- if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) {
- zend_hash_update(
- &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL);
- function_add_ref(function);
-
- phpdbg_notice(
- "Registered %s", lcname);
- } else {
- phpdbg_error("The requested function (%s) could not be found", param->str);
- }
- } else {
- phpdbg_error(
- "The requested name (%s) is already in use", lcname);
- }
-
- efree(lcname);
- } break;
-
- phpdbg_default_switch_case();
+ zend_function *function;
+ char *lcname = zend_str_tolower_dup(param->str, param->len);
+ size_t lcname_len = strlen(lcname);
+
+ if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) {
+ if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) {
+ zend_hash_update(
+ &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL);
+ function_add_ref(function);
+
+ phpdbg_notice(
+ "Registered %s", lcname);
+ } else {
+ phpdbg_error("The requested function (%s) could not be found", param->str);
+ }
+ } else {
+ phpdbg_error(
+ "The requested name (%s) is already in use", lcname);
}
+
+ efree(lcname);
return SUCCESS;
} /* }}} */
@@ -861,14 +916,11 @@ PHPDBG_COMMAND(quit) /* {{{ */
{
/* don't allow this to loop, ever ... */
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
-
- phpdbg_destroy_input((phpdbg_input_t**)&input TSRMLS_CC);
-
PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
zend_bailout();
}
- return SUCCESS;
+ return PHPDBG_NEXT;
} /* }}} */
PHPDBG_COMMAND(clean) /* {{{ */
@@ -908,100 +960,12 @@ PHPDBG_COMMAND(clear) /* {{{ */
return SUCCESS;
} /* }}} */
-PHPDBG_COMMAND(aliases) /* {{{ */
-{
- const phpdbg_command_t *prompt_command = phpdbg_prompt_commands;
-
- phpdbg_help_header();
- phpdbg_writeln("Below are the aliased, short versions of all supported commands");
- while (prompt_command && prompt_command->name) {
- if (prompt_command->alias) {
- if (prompt_command->subs) {
- const phpdbg_command_t *sub_command = prompt_command->subs;
- phpdbg_writeln(EMPTY);
- phpdbg_writeln(" %c -> %9s", prompt_command->alias, prompt_command->name);
- while (sub_command && sub_command->name) {
- if (sub_command->alias) {
- phpdbg_writeln(" |-------- %c -> %15s\t%s", sub_command->alias,
- sub_command->name, sub_command->tip);
- }
- ++sub_command;
- }
- phpdbg_writeln(EMPTY);
- } else {
- phpdbg_writeln(" %c -> %9s\t\t\t%s", prompt_command->alias,
- prompt_command->name, prompt_command->tip);
- }
- }
-
- ++prompt_command;
- }
- phpdbg_help_footer();
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_COMMAND(help) /* {{{ */
-{
- switch (param->type) {
- case EMPTY_PARAM: {
- const phpdbg_command_t *prompt_command = phpdbg_prompt_commands;
- const phpdbg_command_t *help_command = phpdbg_help_commands;
-
- phpdbg_help_header();
- phpdbg_writeln("To get help regarding a specific command type \"help command\"");
-
- phpdbg_notice("Commands");
-
- while (prompt_command && prompt_command->name) {
- phpdbg_writeln(
- " %10s\t%s", prompt_command->name, prompt_command->tip);
- ++prompt_command;
- }
-
- phpdbg_notice("Help Commands");
-
- while (help_command && help_command->name) {
- phpdbg_writeln(" %10s\t%s", help_command->name, help_command->tip);
- ++help_command;
- }
-
- phpdbg_help_footer();
- } break;
-
- default: {
- phpdbg_error(
- "No help can be found for the subject \"%s\"", param->str);
- }
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_COMMAND(quiet) /* {{{ */
-{
- switch (param->type) {
- case NUMERIC_PARAM: {
- if (param->num) {
- PHPDBG_G(flags) |= PHPDBG_IS_QUIET;
- } else {
- PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET;
- }
- phpdbg_notice("Quietness %s",
- (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "enabled" : "disabled");
- } break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
PHPDBG_COMMAND(list) /* {{{ */
{
- switch (param->type) {
+ if (!param) {
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+ } else switch (param->type) {
case NUMERIC_PARAM:
- case EMPTY_PARAM:
return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
case FILE_PARAM:
@@ -1020,54 +984,101 @@ PHPDBG_COMMAND(list) /* {{{ */
return SUCCESS;
} /* }}} */
+PHPDBG_COMMAND(watch) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_list_watchpoints(TSRMLS_C);
+ } else switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_create_var_watchpoint(param->str, param->len TSRMLS_CC) != FAILURE) {
+ phpdbg_notice("Set watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
int phpdbg_interactive(TSRMLS_D) /* {{{ */
{
int ret = SUCCESS;
- phpdbg_input_t *input;
+ char *why = NULL;
+ char *input = NULL;
+ phpdbg_param_t stack;
PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
input = phpdbg_read_input(NULL TSRMLS_CC);
- if (input && input->length > 0L) {
+ if (input) {
do {
- switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
- case FAILURE:
- if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
- if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) {
- phpdbg_error("Failed to execute %s!", input->string);
+ yyscan_t scanner;
+ YY_BUFFER_STATE state;
+
+ phpdbg_init_param(&stack, STACK_PARAM);
+
+ if (yylex_init(&scanner)) {
+ phpdbg_error(
+ "could not initialize scanner");
+ return FAILURE;
+ }
+
+ state = yy_scan_string(input, scanner);
+
+ if (yyparse(&stack, scanner) <= 0) {
+ switch (ret = phpdbg_stack_execute(&stack, &why TSRMLS_CC)) {
+ case FAILURE:
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
+ if (why) {
+ phpdbg_error("%s", why);
+ }
+ }
}
- }
- break;
- case PHPDBG_LEAVE:
- case PHPDBG_FINISH:
- case PHPDBG_UNTIL:
- case PHPDBG_NEXT: {
- if (!EG(in_execution)) {
- phpdbg_error("Not running");
+ if (why) {
+ free(why);
+ why = NULL;
+ }
+ break;
+
+ case PHPDBG_LEAVE:
+ case PHPDBG_FINISH:
+ case PHPDBG_UNTIL:
+ case PHPDBG_NEXT: {
+ if (!EG(in_execution) && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ phpdbg_error("Not running");
+ }
+ goto out;
}
- goto out;
}
}
- phpdbg_destroy_input(&input TSRMLS_CC);
- } while ((input = phpdbg_read_input(NULL TSRMLS_CC)) && (input->length > 0L));
+ if (why) {
+ free(why);
+ why = NULL;
+ }
- if (input && !input->length)
- goto last;
+ yy_delete_buffer(state, scanner);
+ yylex_destroy(scanner);
- } else {
-last:
- if (PHPDBG_G(lcmd)) {
- ret = PHPDBG_G(lcmd)->handler(
- &PHPDBG_G(lparam), input TSRMLS_CC);
- goto out;
- }
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+
+ } while ((input = phpdbg_read_input(NULL TSRMLS_CC)));
}
out:
- phpdbg_destroy_input(&input TSRMLS_CC);
+ if (input) {
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ }
+
+ if (why) {
+ free(why);
+ }
if (EG(in_execution)) {
phpdbg_restore_frame(TSRMLS_C);
@@ -1075,6 +1086,8 @@ out:
PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE;
+ phpdbg_print_changed_zvals(TSRMLS_C);
+
return ret;
} /* }}} */
@@ -1214,7 +1227,7 @@ zend_vm_enter:
); \
} \
\
- do { \
+/* do { */\
switch (phpdbg_interactive(TSRMLS_C)) { \
case PHPDBG_LEAVE: \
case PHPDBG_FINISH: \
@@ -1223,7 +1236,7 @@ zend_vm_enter:
goto next; \
} \
} \
- } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); \
+/* } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); */\
} while (0)
/* allow conditional breakpoints and
@@ -1285,22 +1298,30 @@ zend_vm_enter:
phpdbg_print_opline_ex(
execute_data, &vars, 0 TSRMLS_CC);
+ if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING && (PHPDBG_G(flags) & PHPDBG_STEP_OPCODE || execute_data->opline->lineno != PHPDBG_G(last_line))) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
+ DO_INTERACTIVE();
+ }
+
+ /* check if some watchpoint was hit */
+ {
+ if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) {
+ DO_INTERACTIVE();
+ }
+ }
+
/* search for breakpoints */
{
phpdbg_breakbase_t *brake;
- if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) &&
- (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) {
- phpdbg_hit_breakpoint(
- brake, 1 TSRMLS_CC);
+ if ((PHPDBG_G(flags) & PHPDBG_BP_MASK)
+ && (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))
+ && (brake->type != PHPDBG_BREAK_FILE || execute_data->opline->lineno != PHPDBG_G(last_line))) {
+ phpdbg_hit_breakpoint(brake, 1 TSRMLS_CC);
DO_INTERACTIVE();
}
}
- if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) {
- DO_INTERACTIVE();
- }
-
next:
if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
phpdbg_writeln(EMPTY);
@@ -1309,6 +1330,8 @@ next:
DO_INTERACTIVE();
}
+ PHPDBG_G(last_line) = execute_data->opline->lineno;
+
PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
if (PHPDBG_G(vmret) > 0) {
diff --git a/sapi/phpdbg/phpdbg_prompt.h b/sapi/phpdbg/phpdbg_prompt.h
index 6807d88f41..ef648aabeb 100644
--- a/sapi/phpdbg/phpdbg_prompt.h
+++ b/sapi/phpdbg/phpdbg_prompt.h
@@ -30,11 +30,10 @@ void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
/* {{{ phpdbg command handlers */
PHPDBG_COMMAND(exec);
-PHPDBG_COMMAND(compile);
PHPDBG_COMMAND(step);
-PHPDBG_COMMAND(next);
+PHPDBG_COMMAND(continue);
PHPDBG_COMMAND(run);
-PHPDBG_COMMAND(eval);
+PHPDBG_COMMAND(ev);
PHPDBG_COMMAND(until);
PHPDBG_COMMAND(finish);
PHPDBG_COMMAND(leave);
@@ -47,13 +46,13 @@ PHPDBG_COMMAND(info);
PHPDBG_COMMAND(clean);
PHPDBG_COMMAND(clear);
PHPDBG_COMMAND(help);
-PHPDBG_COMMAND(quiet);
-PHPDBG_COMMAND(aliases);
-PHPDBG_COMMAND(shell);
+PHPDBG_COMMAND(sh);
PHPDBG_COMMAND(set);
PHPDBG_COMMAND(source);
+PHPDBG_COMMAND(export);
PHPDBG_COMMAND(register);
-PHPDBG_COMMAND(quit); /* }}} */
+PHPDBG_COMMAND(quit);
+PHPDBG_COMMAND(watch); /* }}} */
/* {{{ prompt commands */
extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */
diff --git a/sapi/phpdbg/phpdbg_set.c b/sapi/phpdbg/phpdbg_set.c
index 7c4da12a46..54269a8193 100644
--- a/sapi/phpdbg/phpdbg_set.c
+++ b/sapi/phpdbg/phpdbg_set.c
@@ -23,56 +23,52 @@
#include "phpdbg_set.h"
#include "phpdbg_utils.h"
#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-PHPDBG_SET(prompt) /* {{{ */
-{
- switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
- break;
+#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18])
- case STR_PARAM:
- phpdbg_set_prompt(param->str TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
+const phpdbg_command_t phpdbg_set_commands[] = {
+ PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s"),
+#ifndef _WIN32
+ PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss"),
+ PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b"),
+#endif
+ PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s"),
+ PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b"),
+ PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b"),
+ PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b"),
+ PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s"),
+ PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b"),
+ PHPDBG_END_COMMAND
+};
+PHPDBG_SET(prompt) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
+ } else phpdbg_set_prompt(param->str TSRMLS_CC);
+
return SUCCESS;
} /* }}} */
PHPDBG_SET(break) /* {{{ */
{
switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_writeln("%s",
- PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
- break;
-
- case STR_PARAM:
- if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
- phpdbg_enable_breakpoints(TSRMLS_C);
- } else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
- phpdbg_disable_breakpoints(TSRMLS_C);
- }
- break;
-
case NUMERIC_PARAM: {
- if (input->argc > 2) {
- if (phpdbg_argv_is(2, "on")) {
- phpdbg_enable_breakpoint(param->num TSRMLS_CC);
- } else if (phpdbg_argv_is(2, "off")) {
- phpdbg_disable_breakpoint(param->num TSRMLS_CC);
- }
+ if (param->next) {
+ if (param->next->num) {
+ phpdbg_enable_breakpoint(param->num TSRMLS_CC);
+ } else phpdbg_disable_breakpoint(param->num TSRMLS_CC);
} else {
phpdbg_breakbase_t *brake = phpdbg_find_breakbase(param->num TSRMLS_CC);
if (brake) {
phpdbg_writeln(
"%s", brake->disabled ? "off" : "on");
} else {
- phpdbg_error("Failed to find breakpoint #%lx", param->num);
+ phpdbg_error("Failed to find breakpoint #%ld", param->num);
}
}
} break;
@@ -85,104 +81,96 @@ PHPDBG_SET(break) /* {{{ */
return SUCCESS;
} /* }}} */
-#ifndef _WIN32
-PHPDBG_SET(color) /* {{{ */
+PHPDBG_SET(breaks) /* {{{ */
{
- if ((param->type == STR_PARAM) && (input->argc == 3)) {
- const phpdbg_color_t *color = phpdbg_get_color(
- input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
- int element = PHPDBG_COLOR_INVALID;
-
- /* @TODO(anyone) make this consistent with other set commands */
- if (color) {
- if (phpdbg_argv_is(1, "prompt")) {
- phpdbg_notice(
- "setting prompt color to %s (%s)", color->name, color->code);
- element = PHPDBG_COLOR_PROMPT;
- if (PHPDBG_G(prompt)[1]) {
- free(PHPDBG_G(prompt)[1]);
- PHPDBG_G(prompt)[1]=NULL;
- }
- } else if (phpdbg_argv_is(1, "error")) {
- phpdbg_notice(
- "setting error color to %s (%s)", color->name, color->code);
- element = PHPDBG_COLOR_ERROR;
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("%s",
+ PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ phpdbg_enable_breakpoints(TSRMLS_C);
+ } else phpdbg_disable_breakpoints(TSRMLS_C);
+ } break;
- } else if (phpdbg_argv_is(1, "notice")) {
- phpdbg_notice(
- "setting notice color to %s (%s)", color->name, color->code);
- element = PHPDBG_COLOR_NOTICE;
+ default:
+ phpdbg_error(
+ "set break used incorrectly: set break [id] <on|off>");
+ }
- } else goto usage;
+ return SUCCESS;
+} /* }}} */
- /* set color for element */
- phpdbg_set_color(element, color TSRMLS_CC);
- } else {
- phpdbg_error(
- "Failed to find the requested color (%s)", input->argv[2]->string);
- }
- } else {
-usage:
+#ifndef _WIN32
+PHPDBG_SET(color) /* {{{ */
+{
+ const phpdbg_color_t *color = phpdbg_get_color(
+ param->next->str, param->next->len TSRMLS_CC);
+
+ if (!color) {
phpdbg_error(
- "set color used incorrectly: set color <prompt|error|notice> <color>");
+ "Failed to find the requested color (%s)", param->next->str);
+ return SUCCESS;
}
+
+ switch (phpdbg_get_element(param->str, param->len TSRMLS_CC)) {
+ case PHPDBG_COLOR_PROMPT:
+ phpdbg_notice(
+ "setting prompt color to %s (%s)", color->name, color->code);
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ PHPDBG_G(prompt)[1]=NULL;
+ }
+ phpdbg_set_color(PHPDBG_COLOR_PROMPT, color TSRMLS_CC);
+ break;
+
+ case PHPDBG_COLOR_ERROR:
+ phpdbg_notice(
+ "setting error color to %s (%s)", color->name, color->code);
+ phpdbg_set_color(PHPDBG_COLOR_ERROR, color TSRMLS_CC);
+ break;
+
+ case PHPDBG_COLOR_NOTICE:
+ phpdbg_notice(
+ "setting notice color to %s (%s)", color->name, color->code);
+ phpdbg_set_color(PHPDBG_COLOR_NOTICE, color TSRMLS_CC);
+ break;
+
+ default:
+ phpdbg_error(
+ "Failed to find the requested element (%s)", param->str);
+ }
+
return SUCCESS;
} /* }}} */
PHPDBG_SET(colors) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM: {
- phpdbg_writeln(
- "%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
- goto done;
- }
-
- case STR_PARAM: {
- if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
PHPDBG_G(flags) |= PHPDBG_IS_COLOURED;
- goto done;
- } else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
+ } else {
PHPDBG_G(flags) &= ~PHPDBG_IS_COLOURED;
- goto done;
}
- }
+ } break;
default:
phpdbg_error(
"set colors used incorrectly: set colors <on|off>");
}
-done:
return SUCCESS;
} /* }}} */
#endif
PHPDBG_SET(oplog) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_notice(
- "Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
- break;
-
- case NUMERIC_PARAM: switch (param->num) {
- case 1:
- phpdbg_error(
- "An output file must be provided to enable oplog");
- break;
-
- case 0: {
- if (PHPDBG_G(oplog)) {
- phpdbg_notice("Disabling oplog");
- fclose(
- PHPDBG_G(oplog));
- } else {
- phpdbg_error("Oplog is not enabled!");
- }
- } break;
- } break;
-
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_notice("Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
+ } else switch (param->type) {
case STR_PARAM: {
/* open oplog */
FILE *old = PHPDBG_G(oplog);
@@ -206,3 +194,65 @@ PHPDBG_SET(oplog) /* {{{ */
return SUCCESS;
} /* }}} */
+PHPDBG_SET(quiet) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("Quietness %s",
+ PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUIET;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET;
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(stepping) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("Stepping %s",
+ PHPDBG_G(flags) & PHPDBG_STEP_OPCODE ? "opcode" : "line");
+ } else switch (param->type) {
+ case STR_PARAM: {
+ if ((param->len == sizeof("opcode")-1) &&
+ (memcmp(param->str, "opcode", sizeof("opcode")) == SUCCESS)) {
+ PHPDBG_G(flags) |= PHPDBG_STEP_OPCODE;
+ } else if ((param->len == sizeof("line")-1) &&
+ (memcmp(param->str, "line", sizeof("line")) == SUCCESS)) {
+ PHPDBG_G(flags) &= ~PHPDBG_STEP_OPCODE;
+ } else {
+ phpdbg_error("usage set stepping [<opcode|line>]");
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(refcount) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("Refcount %s", PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ PHPDBG_G(flags) |= PHPDBG_SHOW_REFCOUNTS;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_SHOW_REFCOUNTS;
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_set.h b/sapi/phpdbg/phpdbg_set.h
index 120aeb34f4..dea61ed382 100644
--- a/sapi/phpdbg/phpdbg_set.h
+++ b/sapi/phpdbg/phpdbg_set.h
@@ -32,16 +32,11 @@ PHPDBG_SET(colors);
#endif
PHPDBG_SET(oplog);
PHPDBG_SET(break);
+PHPDBG_SET(breaks);
+PHPDBG_SET(quiet);
+PHPDBG_SET(stepping);
+PHPDBG_SET(refcount);
-static const phpdbg_command_t phpdbg_set_commands[] = {
- PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt <string>", 'p', set_prompt, NULL, 0),
-#ifndef _WIN32
- PHPDBG_COMMAND_D_EX(color, "usage: set color <element> <color>", 'c', set_color, NULL, 1),
- PHPDBG_COMMAND_D_EX(colors, "usage: set colors <on|off>", 'C', set_colors, NULL, 1),
-#endif
- PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog <output>", 'O', set_oplog, NULL, 0),
- PHPDBG_COMMAND_D_EX(break, "usage: set break [id] <on|off>", 'b', set_break, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_set_commands[];
#endif /* PHPDBG_SET_H */
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index 1effcfccaf..c9b22a2039 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -30,6 +30,8 @@
#ifdef _WIN32
# include "win32/time.h"
+#elif defined(HAVE_SYS_IOCTL_H)
+# include "sys/ioctl.h"
#endif
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
@@ -65,6 +67,14 @@ const static phpdbg_color_t colors[] = {
PHPDBG_COLOR_END
}; /* }}} */
+/* {{{ */
+const static phpdbg_element_t elements[] = {
+ PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT),
+ PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR),
+ PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE),
+ PHPDBG_ELEMENT_END
+}; /* }}} */
+
PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */
{
if (!str)
@@ -347,6 +357,21 @@ PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D) /* {{{ */
return colors;
} /* }}} */
+PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC) {
+ const phpdbg_element_t *element = elements;
+
+ while (element && element->name) {
+ if (len == element->name_length) {
+ if (strncasecmp(name, element->name, len) == SUCCESS) {
+ return element->id;
+ }
+ }
+ element++;
+ }
+
+ return PHPDBG_COLOR_INVALID;
+}
+
PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */
{
/* free formatted prompt */
@@ -385,3 +410,39 @@ PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
return PHPDBG_G(prompt)[1];
} /* }}} */
+
+int phpdbg_rebuild_symtable(TSRMLS_D) {
+ if (!EG(active_op_array)) {
+ phpdbg_error("No active op array!");
+ return FAILURE;
+ }
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+
+ if (!EG(active_symbol_table)) {
+ phpdbg_error("No active symbol table!");
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
+PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */
+{
+ int columns;
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+ columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+#elif defined(HAVE_SYS_IOCTL_H)
+ struct winsize w;
+
+ columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 100;
+#else
+ columns = 100;
+#endif
+ return columns;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_utils.h b/sapi/phpdbg/phpdbg_utils.h
index c5164c3ac3..68ae7e44a3 100644
--- a/sapi/phpdbg/phpdbg_utils.h
+++ b/sapi/phpdbg/phpdbg_utils.h
@@ -85,12 +85,17 @@ PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...);
{color, sizeof(color)-1, code}
#define PHPDBG_COLOR_END \
{NULL, 0L, {0}}
+#define PHPDBG_ELEMENT_LEN 3
+#define PHPDBG_ELEMENT_D(name, id) \
+ {name, sizeof(name)-1, id}
+#define PHPDBG_ELEMENT_END \
+ {NULL, 0L, 0}
#define PHPDBG_COLOR_INVALID -1
-#define PHPDBG_COLOR_PROMPT 0
-#define PHPDBG_COLOR_ERROR 1
-#define PHPDBG_COLOR_NOTICE 2
-#define PHPDBG_COLORS 3
+#define PHPDBG_COLOR_PROMPT 0
+#define PHPDBG_COLOR_ERROR 1
+#define PHPDBG_COLOR_NOTICE 2
+#define PHPDBG_COLORS 3
typedef struct _phpdbg_color_t {
char *name;
@@ -98,13 +103,25 @@ typedef struct _phpdbg_color_t {
const char code[PHPDBG_COLOR_LEN];
} phpdbg_color_t;
+typedef struct _phpdbg_element_t {
+ char *name;
+ size_t name_length;
+ int id;
+} phpdbg_element_t;
+
PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC);
PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC);
PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC);
-PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); /* }}} */
+PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D);
+PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC); /* }}} */
/* {{{ Prompt Management */
PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC);
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */
+/* {{{ Console Width */
+PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */
+
+int phpdbg_rebuild_symtable(TSRMLS_D);
+
#endif /* PHPDBG_UTILS_H */
diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c
new file mode 100644
index 0000000000..a6bf6289bf
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_watch.c
@@ -0,0 +1,789 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+#include "phpdbg_btree.h"
+#include "phpdbg_watch.h"
+#include "phpdbg_utils.h"
+#ifndef _WIN32
+# include <unistd.h>
+# include <sys/mman.h>
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+
+typedef struct {
+ void *page;
+ size_t size;
+ char reenable_writing;
+ /* data must be last element */
+ void *data;
+} phpdbg_watch_memdump;
+
+#define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
+
+
+static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) {
+ phpdbg_watchpoint_t *watch;
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ watch = result->ptr;
+
+ /* check if that addr is in a mprotect()'ed memory area */
+ if ((char *)phpdbg_get_page_boundary(watch->addr.ptr) > (char *)addr || (char *)phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *)addr) {
+ /* failure */
+ return NULL;
+ }
+
+ return watch;
+}
+
+static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access TSRMLS_DC) {
+ int m;
+
+ /* pagesize is assumed to be in the range of 2^x */
+ m = mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access);
+}
+
+static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_change_watchpoint_access(watch, PROT_READ TSRMLS_CC);
+}
+
+static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE TSRMLS_CC);
+}
+
+static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch);
+}
+
+static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr);
+}
+
+void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
+ watch->addr.ptr = addr;
+ watch->size = size;
+}
+
+void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
+ phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch);
+ watch->type = WATCH_ON_ZVAL;
+}
+
+void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
+ phpdbg_create_addr_watchpoint(ht, sizeof(HashTable), watch);
+ watch->type = WATCH_ON_HASHTABLE;
+}
+
+void phpdbg_watch_HashTable_dtor(zval **ptr);
+
+static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ watch->flags |= PHPDBG_WATCH_SIMPLE;
+
+ phpdbg_store_watchpoint(watch TSRMLS_CC);
+ zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL);
+
+ if (watch->type == WATCH_ON_ZVAL) {
+ phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong)watch->parent_container, watch->parent_container->pDestructor);
+ watch->parent_container->pDestructor = (dtor_func_t)phpdbg_watch_HashTable_dtor;
+ }
+
+ phpdbg_activate_watchpoint(watch TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ HashTable *ht;
+
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ default:
+ return FAILURE;
+ }
+
+ phpdbg_create_ht_watchpoint(ht, watch);
+
+ phpdbg_create_watchpoint(watch TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+static char *phpdbg_get_property_key(char *key) {
+ if (*key != 0) {
+ return key;
+ }
+ return strchr(key + 1, 0) + 1;
+}
+
+static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ HashTable *ht;
+
+ if (watch->type != WATCH_ON_ZVAL) {
+ return FAILURE;
+ }
+
+ watch->flags |= PHPDBG_WATCH_RECURSIVE;
+ phpdbg_create_watchpoint(watch TSRMLS_CC);
+
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ default:
+ return SUCCESS;
+ }
+
+ {
+ HashPosition position;
+ zval **zv;
+ zval key;
+
+ for (zend_hash_internal_pointer_reset_ex(ht, &position);
+ zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
+ zend_hash_move_forward_ex(ht, &position)) {
+ phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
+
+ new_watch->flags = PHPDBG_WATCH_RECURSIVE;
+ new_watch->parent = watch;
+ new_watch->parent_container = ht;
+
+ zend_hash_get_current_key_zval_ex(ht, &key, &position);
+ if (Z_TYPE(key) == IS_STRING) {
+ new_watch->name_in_parent = zend_strndup(Z_STRVAL(key), Z_STRLEN(key));
+ new_watch->name_in_parent_len = Z_STRLEN(key);
+ } else {
+ new_watch->name_in_parent = NULL;
+ new_watch->name_in_parent_len = asprintf(&new_watch->name_in_parent, "%ld", Z_LVAL(key));
+ }
+
+ new_watch->str = NULL;
+ new_watch->str_len = asprintf(&new_watch->str, "%.*s%s%s%s", (int)watch->str_len, watch->str, Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(new_watch->name_in_parent), Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"]":"");
+
+ phpdbg_create_zval_watchpoint(*zv, new_watch);
+ phpdbg_create_recursive_watchpoint(new_watch TSRMLS_CC);
+ }
+ }
+
+ {
+ phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
+
+ new_watch->parent = watch;
+ new_watch->parent_container = watch->parent_container;
+ new_watch->name_in_parent = zend_strndup(watch->name_in_parent, watch->name_in_parent_len);
+ new_watch->name_in_parent_len = watch->name_in_parent_len;
+ new_watch->str = NULL;
+ new_watch->str_len = asprintf(&new_watch->str, "%.*s[]", (int)watch->str_len, watch->str);
+ new_watch->flags = PHPDBG_WATCH_RECURSIVE;
+
+ phpdbg_create_ht_watchpoint(ht, new_watch);
+ phpdbg_create_watchpoint(new_watch TSRMLS_CC);
+ }
+
+ return SUCCESS;
+}
+
+static int phpdbg_delete_watchpoint_recursive(phpdbg_watchpoint_t *watch, zend_bool user_request TSRMLS_DC) {
+ if (watch->type == WATCH_ON_HASHTABLE || (watch->type == WATCH_ON_ZVAL && (Z_TYPE_P(watch->addr.zv) == IS_ARRAY || Z_TYPE_P(watch->addr.zv) == IS_OBJECT))) {
+ HashTable *ht;
+ phpdbg_btree_result *result;
+
+ if (watch->type == WATCH_ON_HASHTABLE && user_request) {
+ HashPosition position;
+ zval **zv;
+ zval key;
+ char *str;
+ int str_len;
+ phpdbg_watchpoint_t **watchpoint;
+
+ ht = watch->addr.ht;
+
+ for (zend_hash_internal_pointer_reset_ex(ht, &position);
+ zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
+ zend_hash_move_forward_ex(ht, &position)) {
+ zend_hash_get_current_key_zval_ex(ht, &key, &position);
+ str = NULL;
+ if (Z_TYPE(key) == IS_STRING) {
+ str_len = asprintf(&str, "%.*s%s%s%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(Z_STRVAL(key)), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
+ } else {
+ str_len = asprintf(&str, "%.*s%s%li%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", Z_LVAL(key), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
+ }
+
+ if (zend_hash_find(&PHPDBG_G(watchpoints), str, str_len, (void **) &watchpoint) == SUCCESS) {
+ phpdbg_delete_watchpoint_recursive(*watchpoint, 1 TSRMLS_CC);
+ }
+ }
+ } else {
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ }
+
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ht))) {
+ phpdbg_delete_watchpoint_recursive((phpdbg_watchpoint_t *) result->ptr, user_request TSRMLS_CC);
+ }
+ }
+ }
+
+ return zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+}
+
+static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) {
+ int ret;
+ phpdbg_watchpoint_t *watch;
+ phpdbg_btree_result *result;
+
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)tmp_watch->addr.ptr)) == NULL) {
+ return FAILURE;
+ }
+
+ watch = result->ptr;
+
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ ret = phpdbg_delete_watchpoint_recursive(watch, 1 TSRMLS_CC);
+ } else {
+ ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+
+ free(tmp_watch->str);
+ efree(tmp_watch);
+
+ return ret;
+}
+
+static int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC), zend_bool silent TSRMLS_DC) {
+ int ret = FAILURE;
+ zend_bool new_index = 1;
+ char *last_index;
+ int index_len = 0;
+ zval **zv;
+
+ if (len < 2 || *input != '$') {
+ goto error;
+ }
+
+ while (i++ < len) {
+ if (i == len) {
+ new_index = 1;
+ } else {
+ switch (input[i]) {
+ case '[':
+ new_index = 1;
+ break;
+ case ']':
+ break;
+ case '>':
+ if (last_index[index_len - 1] == '-') {
+ new_index = 1;
+ index_len--;
+ }
+ break;
+
+ default:
+ if (new_index) {
+ last_index = input + i;
+ new_index = 0;
+ }
+ if (input[i - 1] == ']') {
+ goto error;
+ }
+ index_len++;
+ }
+ }
+
+ if (new_index && index_len == 0) {
+ HashPosition position;
+ for (zend_hash_internal_pointer_reset_ex(parent, &position);
+ zend_hash_get_current_data_ex(parent, (void **)&zv, &position) == SUCCESS;
+ zend_hash_move_forward_ex(parent, &position)) {
+ if (i == len || (i == len - 1 && input[len - 1] == ']')) {
+ zval *key = emalloc(sizeof(zval));
+ phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ watch->flags = 0;
+ zend_hash_get_current_key_zval_ex(parent, key, &position);
+ convert_to_string(key);
+ watch->str = malloc(i + Z_STRLEN_P(key) + 2);
+ watch->str_len = sprintf(watch->str, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":"");
+ efree(key);
+ watch->name_in_parent = zend_strndup(last_index, index_len);
+ watch->name_in_parent_len = index_len;
+ watch->parent_container = parent;
+ phpdbg_create_zval_watchpoint(*zv, watch);
+
+ ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
+ } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
+ phpdbg_watchpoint_parse_input(input, len, Z_OBJPROP_PP(zv), i, callback, silent TSRMLS_CC);
+ } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
+ phpdbg_watchpoint_parse_input(input, len, Z_ARRVAL_PP(zv), i, callback, silent TSRMLS_CC);
+ } else {
+ /* Ignore silently */
+ }
+ }
+ return ret;
+ } else if (new_index) {
+ char last_chr = last_index[index_len];
+ last_index[index_len] = 0;
+ if (zend_symtable_find(parent, last_index, index_len + 1, (void **)&zv) == FAILURE) {
+ if (!silent) {
+ phpdbg_error("%.*s is undefined", (int)i, input);
+ }
+ return FAILURE;
+ }
+ last_index[index_len] = last_chr;
+ if (i == len) {
+ phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ watch->flags = 0;
+ watch->str = zend_strndup(input, len);
+ watch->str_len = len;
+ watch->name_in_parent = zend_strndup(last_index, index_len);
+ watch->name_in_parent_len = index_len;
+ watch->parent_container = parent;
+ phpdbg_create_zval_watchpoint(*zv, watch);
+
+ ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
+ } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
+ parent = Z_OBJPROP_PP(zv);
+ } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
+ parent = Z_ARRVAL_PP(zv);
+ } else {
+ phpdbg_error("%.*s is nor an array nor an object", (int)i, input);
+ return FAILURE;
+ }
+ index_len = 0;
+ }
+ }
+
+ return ret;
+ error:
+ phpdbg_error("Malformed input");
+ return FAILURE;
+}
+
+static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
+ if (EG(This) && len >= 5 && !memcmp("$this", input, 5)) {
+ zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), NULL);
+ }
+
+ if (zend_is_auto_global(input, len TSRMLS_CC) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, callback, 1 TSRMLS_CC) != FAILURE) {
+ return SUCCESS;
+ }
+
+ return phpdbg_watchpoint_parse_input(input, len, EG(active_symbol_table), 0, callback, 0 TSRMLS_CC);
+}
+
+PHPDBG_WATCH(delete) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_delete_var_watchpoint(param->str, param->len TSRMLS_CC) == FAILURE) {
+ phpdbg_error("Nothing was deleted, no corresponding watchpoint found");
+ } else {
+ phpdbg_notice("Removed watchpoint %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_WATCH(recursive) /* {{{ */
+{
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint TSRMLS_CC) != FAILURE) {
+ phpdbg_notice("Set recursive watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_WATCH(array) /* {{{ */
+{
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint TSRMLS_CC) != FAILURE) {
+ phpdbg_notice("Set array watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+void phpdbg_watch_HashTable_dtor(zval **zv) {
+ TSRMLS_FETCH();
+
+ phpdbg_btree_result *result;
+ zval_ptr_dtor_wrapper(zv);
+
+
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)*zv))) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+
+ PHPDBG_G(watchpoint_hit) = 1;
+
+ phpdbg_notice("%.*s was removed, removing watchpoint%s", (int)watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_RECURSIVE)?" recursively":"");
+
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_delete_watchpoint_recursive(watch, 0 TSRMLS_CC);
+ } else {
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+ }
+}
+
+
+int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC) {
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+
+ return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_create_watchpoint TSRMLS_CC);
+}
+
+int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC) {
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+
+ return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_delete_watchpoint TSRMLS_CC);
+}
+
+#ifdef _WIN32
+int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC) {
+#else
+int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) {
+#endif
+ void *page;
+ phpdbg_watch_memdump *dump;
+ phpdbg_watchpoint_t *watch;
+ size_t size;
+
+ watch = phpdbg_check_for_watchpoint(
+#ifdef _WIN32
+ addr
+#else
+ info->si_addr
+#endif
+ TSRMLS_CC);
+
+ if (watch == NULL) {
+ return FAILURE;
+ }
+
+ page = phpdbg_get_page_boundary(watch->addr.ptr);
+ size = phpdbg_get_total_page_size(watch->addr.ptr, watch->size);
+
+ /* re-enable writing */
+ mprotect(page, size, PROT_READ | PROT_WRITE);
+
+ dump = malloc(MEMDUMP_SIZE(size));
+ dump->page = page;
+ dump->size = size;
+
+ memcpy(&dump->data, page, size);
+
+ zend_llist_add_element(&PHPDBG_G(watchlist_mem), &dump);
+
+ return SUCCESS;
+}
+
+void phpdbg_watchpoints_clean(TSRMLS_D) {
+ zend_hash_clean(&PHPDBG_G(watchpoints));
+}
+
+static void phpdbg_watch_dtor(void *pDest) {
+ TSRMLS_FETCH();
+
+ phpdbg_watchpoint_t *watch = *(phpdbg_watchpoint_t **)pDest;
+
+ phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
+ phpdbg_remove_watchpoint(watch TSRMLS_CC);
+
+ free(watch->str);
+ free(watch->name_in_parent);
+ efree(watch);
+}
+
+static void phpdbg_watch_mem_dtor(void *llist_data) {
+ phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data;
+
+ /* Disble writing again */
+ if (dump->reenable_writing) {
+ mprotect(dump->page, dump->size, PROT_READ);
+ }
+
+ free(*(void **)llist_data);
+}
+
+void phpdbg_setup_watchpoints(TSRMLS_D) {
+#if _SC_PAGE_SIZE
+ phpdbg_pagesize = sysconf(_SC_PAGE_SIZE);
+#elif _SC_PAGESIZE
+ phpdbg_pagesize = sysconf(_SC_PAGESIZE);
+#elif _SC_NUTC_OS_PAGESIZE
+ phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE);
+#else
+ phpdbg_pagesize = 4096; /* common pagesize */
+#endif
+
+ zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 1);
+ phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8);
+ phpdbg_btree_init(&PHPDBG_G(watch_HashTables), sizeof(void *) * 8);
+ _zend_hash_init(&PHPDBG_G(watchpoints), 8, phpdbg_watch_dtor, 0 ZEND_FILE_LINE_CC);
+}
+
+static void phpdbg_print_changed_zval(phpdbg_watch_memdump *dump TSRMLS_DC) {
+ /* fetch all changes between dump->page and dump->page + dump->size */
+ phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), (zend_ulong)dump->page, (zend_ulong)dump->page + dump->size);
+ phpdbg_btree_result *result, *htresult;
+ int elementDiff;
+ void *curTest;
+
+ dump->reenable_writing = 0;
+
+ while ((result = phpdbg_btree_next(&pos))) {
+ phpdbg_watchpoint_t *watch = result->ptr, *htwatch;
+ void *oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page);
+ char reenable = 1;
+
+ if (watch->addr.ptr < dump->page || watch->addr.ptr + watch->size > dump->page + dump->size) {
+ continue;
+ }
+
+ /* Test if the zval was separated and if necessary move the watchpoint */
+ if (zend_hash_find(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1, &curTest) == SUCCESS) {
+ if (watch->type == WATCH_ON_HASHTABLE) {
+ switch (Z_TYPE_PP((zval **)curTest)) {
+ case IS_ARRAY:
+ curTest = (void *)Z_ARRVAL_PP((zval **)curTest);
+ break;
+ case IS_OBJECT:
+ curTest = (void *)Z_OBJPROP_PP((zval **)curTest);
+ break;
+ }
+ } else {
+ curTest = *(void **)curTest;
+ }
+
+ if (curTest != watch->addr.ptr) {
+ phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
+ phpdbg_remove_watchpoint(watch TSRMLS_CC);
+ watch->addr.ptr = curTest;
+ phpdbg_store_watchpoint(watch TSRMLS_CC);
+ phpdbg_activate_watchpoint(watch TSRMLS_CC);
+
+ reenable = 0;
+ }
+ }
+
+ /* Show to the user what changed and delete watchpoint upon removal */
+ if (memcmp(oldPtr, watch->addr.ptr, watch->size) != SUCCESS) {
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS || (watch->type == WATCH_ON_ZVAL && memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value))) || (watch->type == WATCH_ON_HASHTABLE
+#if ZEND_DEBUG
+ && !watch->addr.ht->inconsistent
+#endif
+ && zend_hash_num_elements((HashTable *)oldPtr) != zend_hash_num_elements(watch->addr.ht))) {
+ PHPDBG_G(watchpoint_hit) = 1;
+
+ phpdbg_notice("Breaking on watchpoint %s", watch->str);
+ }
+
+ switch (watch->type) {
+ case WATCH_ON_ZVAL: {
+ int removed = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc && !zend_symtable_exists(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1);
+ int show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value));
+ int show_ref = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc || ((zval *)oldPtr)->is_ref__gc != watch->addr.zv->is_ref__gc;
+
+ if (removed || show_value) {
+ phpdbg_write("Old value: ");
+ if ((Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) && removed) {
+ phpdbg_writeln("Value inaccessible, HashTable already destroyed");
+ } else {
+ zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC);
+ phpdbg_writeln("");
+ }
+ }
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && (removed || show_ref)) {
+ phpdbg_writeln("Old refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc);
+ }
+
+ /* check if zval was removed */
+ if (removed) {
+ phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+
+ reenable = 0;
+
+ if (Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) {
+ goto remove_ht_watch;
+ }
+
+ break;
+ }
+
+ if (show_value) {
+ phpdbg_write("New value: ");
+ zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC);
+ phpdbg_writeln("");
+ }
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && show_ref) {
+ phpdbg_writeln("New refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc);
+ }
+
+ if ((Z_TYPE_P(watch->addr.zv) == IS_ARRAY && Z_ARRVAL_P(watch->addr.zv) != Z_ARRVAL_P((zval *)oldPtr)) || (Z_TYPE_P(watch->addr.zv) != IS_OBJECT && Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
+ /* add new watchpoints if necessary */
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
+ }
+ }
+
+ if ((Z_TYPE_P((zval *)oldPtr) != IS_ARRAY || Z_ARRVAL_P(watch->addr.zv) == Z_ARRVAL_P((zval *)oldPtr)) && (Z_TYPE_P((zval *)oldPtr) != IS_OBJECT || Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
+ break;
+ }
+
+remove_ht_watch:
+ if ((htresult = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)Z_ARRVAL_P((zval *)oldPtr)))) {
+ htwatch = htresult->ptr;
+ zend_hash_del(&PHPDBG_G(watchpoints), htwatch->str, htwatch->str_len);
+ }
+
+ break;
+ }
+ case WATCH_ON_HASHTABLE:
+
+#ifdef ZEND_DEBUG
+ if (watch->addr.ht->inconsistent) {
+ phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+
+ reenable = 0;
+
+ break;
+ }
+#endif
+
+ elementDiff = zend_hash_num_elements((HashTable *)oldPtr) - zend_hash_num_elements(watch->addr.ht);
+ if (elementDiff) {
+ if (elementDiff > 0) {
+ phpdbg_writeln("%d elements were removed from the array", elementDiff);
+ } else {
+ phpdbg_writeln("%d elements were added to the array", -elementDiff);
+
+ /* add new watchpoints if necessary */
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
+ }
+ }
+ }
+ if (((HashTable *)oldPtr)->pInternalPointer != watch->addr.ht->pInternalPointer) {
+ phpdbg_writeln("Internal pointer of array was changed");
+ }
+ break;
+ }
+ }
+
+ dump->reenable_writing = dump->reenable_writing | reenable;
+ }
+}
+
+int phpdbg_print_changed_zvals(TSRMLS_D) {
+ zend_llist_position pos;
+ phpdbg_watch_memdump **dump;
+ int ret;
+
+ if (zend_llist_count(&PHPDBG_G(watchlist_mem)) == 0) {
+ return FAILURE;
+ }
+
+ dump = (phpdbg_watch_memdump **)zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos);
+
+ do {
+ phpdbg_print_changed_zval(*dump TSRMLS_CC);
+ } while ((dump = (phpdbg_watch_memdump **)zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos)));
+
+ zend_llist_clean(&PHPDBG_G(watchlist_mem));
+
+ ret = PHPDBG_G(watchpoint_hit)?SUCCESS:FAILURE;
+ PHPDBG_G(watchpoint_hit) = 0;
+
+ return ret;
+}
+
+void phpdbg_list_watchpoints(TSRMLS_D) {
+ HashPosition position;
+ phpdbg_watchpoint_t **watch;
+
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(watchpoints), &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(watchpoints), (void**) &watch, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(watchpoints), &position)) {
+ phpdbg_writeln("%.*s", (int)(*watch)->str_len, (*watch)->str);
+ }
+}
+
+void phpdbg_watch_efree(void *ptr) {
+ TSRMLS_FETCH();
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)ptr);
+
+ if (result) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+
+ if ((size_t)watch->addr.ptr + watch->size > (size_t)ptr) {
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+ }
+
+ PHPDBG_G(original_free_function)(ptr);
+}
diff --git a/sapi/phpdbg/phpdbg_watch.h b/sapi/phpdbg/phpdbg_watch.h
new file mode 100644
index 0000000000..d00bcff77e
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_watch.h
@@ -0,0 +1,112 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WATCH_H
+#define PHPDBG_WATCH_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#ifdef _WIN32
+# include "phpdbg_win.h"
+#endif
+
+#define PHPDBG_WATCH(name) PHPDBG_COMMAND(watch_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_WATCH(array);
+PHPDBG_WATCH(delete);
+PHPDBG_WATCH(recursive);
+
+/**
+ * Commands
+ */
+
+static const phpdbg_command_t phpdbg_watch_commands[] = {
+ PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s"),
+ PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s"),
+ PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s"),
+ PHPDBG_END_COMMAND
+};
+
+/* Watchpoint functions/typedefs */
+
+typedef enum {
+ WATCH_ON_ZVAL,
+ WATCH_ON_HASHTABLE,
+} phpdbg_watchtype;
+
+
+#define PHPDBG_WATCH_SIMPLE 0x0
+#define PHPDBG_WATCH_RECURSIVE 0x1
+
+typedef struct _phpdbg_watchpoint_t phpdbg_watchpoint_t;
+
+struct _phpdbg_watchpoint_t {
+ phpdbg_watchpoint_t *parent;
+ HashTable *parent_container;
+ char *name_in_parent;
+ size_t name_in_parent_len;
+ char *str;
+ size_t str_len;
+ union {
+ zval *zv;
+ HashTable *ht;
+ void *ptr;
+ } addr;
+ size_t size;
+ phpdbg_watchtype type;
+ char flags;
+};
+
+void phpdbg_setup_watchpoints(TSRMLS_D);
+
+#ifndef _WIN32
+int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC);
+#else
+int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC);
+#endif
+
+void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch);
+void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch);
+
+int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC);
+int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC);
+
+int phpdbg_print_changed_zvals(TSRMLS_D);
+
+void phpdbg_list_watchpoints(TSRMLS_D);
+
+void phpdbg_watch_efree(void *ptr);
+
+
+static long phpdbg_pagesize;
+
+static zend_always_inline void *phpdbg_get_page_boundary(void *addr) {
+ return (void *)((size_t)addr & ~(phpdbg_pagesize - 1));
+}
+
+static zend_always_inline size_t phpdbg_get_total_page_size(void *addr, size_t size) {
+ return (size_t)phpdbg_get_page_boundary((void *)((size_t)addr + size - 1)) - (size_t)phpdbg_get_page_boundary(addr) + phpdbg_pagesize;
+}
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_win.c b/sapi/phpdbg/phpdbg_win.c
new file mode 100644
index 0000000000..b0cbdf267a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_win.c
@@ -0,0 +1,42 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+
+int mprotect(void *addr, size_t size, int protection) {
+ int var;
+ return (int)VirtualProtect(addr, size, protection == (PROT_READ | PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, &var);
+}
+
+int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp) {
+ EXCEPTION_RECORD *xr = xp->ExceptionRecord;
+ CONTEXT *xc = xp->ContextRecord;
+
+ if(xr->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
+ TSRMLS_FETCH();
+
+ if (phpdbg_watchpoint_segfault_handler((void *)xr->ExceptionInformation[1] TSRMLS_CC) == SUCCESS) {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
diff --git a/sapi/phpdbg/phpdbg_win.h b/sapi/phpdbg/phpdbg_win.h
new file mode 100644
index 0000000000..68c3052790
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_win.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WIN_H
+#define PHPDBG_WIN_H
+
+#include "winbase.h"
+#include "windows.h"
+#include "excpt.h"
+
+#define PROT_READ 1
+#define PROT_WRITE 2
+
+int mprotect(void *addr, size_t size, int protection);
+
+void phpdbg_win_set_mm_heap(zend_mm_heap *heap);
+
+int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp);
+
+#endif \ No newline at end of file
diff --git a/sapi/phpdbg/test.php b/sapi/phpdbg/test.php
index 5fdbcbe1a4..d93c81a89a 100644
--- a/sapi/phpdbg/test.php
+++ b/sapi/phpdbg/test.php
@@ -6,11 +6,16 @@ if (isset($include)) {
$stdout = fopen("php://stdout", "w+");
class phpdbg {
- public function isGreat($greeting = null) {
- printf(
- "%s: %s\n", __METHOD__, $greeting);
- return $this;
- }
+ private $sprintf = "%s: %s\n";
+
+ public function isGreat($greeting = null) {
+ printf($this->sprintf, __METHOD__, $greeting);
+ return $this;
+ }
+}
+
+function mine() {
+ var_dump(func_get_args());
}
function test($x, $y = 0) {
@@ -49,3 +54,34 @@ function phpdbg_test_ob()
echo 'End';
echo $b;
}
+
+$array = [
+ 1,
+ 2,
+ [3, 4],
+ [5, 6],
+];
+
+$array[] = 7;
+
+array_walk($array, function (&$item) {
+ if (is_array($item))
+ $item[0] += 2;
+ else
+ $item -= 1;
+});
+
+class testClass {
+ public $a = 2;
+ protected $b = [1, 3];
+ private $c = 7;
+}
+
+$obj = new testClass;
+
+$test = $obj->a;
+
+$obj->a += 2;
+$test -= 2;
+
+unset($obj);