]> granicus.if.org Git - php/commitdiff
Merge sapi/phpdbg into PHP-5.6
authorBob Weinand <bobwei9@hotmail.com>
Mon, 21 Apr 2014 21:29:25 +0000 (23:29 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Mon, 21 Apr 2014 21:30:08 +0000 (23:30 +0200)
39 files changed:
.gitignore
Makefile.frag
config.m4
config.w32
phpdbg.c
phpdbg.h
phpdbg_bp.c
phpdbg_bp.h
phpdbg_break.c
phpdbg_break.h
phpdbg_btree.c [new file with mode: 0644]
phpdbg_btree.h [new file with mode: 0644]
phpdbg_cmd.c
phpdbg_cmd.h
phpdbg_frame.c
phpdbg_help.c
phpdbg_help.h
phpdbg_info.c
phpdbg_info.h
phpdbg_lexer.c [new file with mode: 0644]
phpdbg_lexer.h [new file with mode: 0644]
phpdbg_lexer.l [new file with mode: 0644]
phpdbg_list.c
phpdbg_list.h
phpdbg_opcode.c
phpdbg_parser.y [new file with mode: 0644]
phpdbg_print.c
phpdbg_print.h
phpdbg_prompt.c
phpdbg_prompt.h
phpdbg_set.c
phpdbg_set.h
phpdbg_utils.c
phpdbg_utils.h
phpdbg_watch.c [new file with mode: 0644]
phpdbg_watch.h [new file with mode: 0644]
phpdbg_win.c [new file with mode: 0644]
phpdbg_win.h [new file with mode: 0644]
test.php

index 297efcbc420ea23282dbcc04087b73ae693e27c8..af445861acdbb1efc63ddcd53d2a309a535e454c 100644 (file)
@@ -1,5 +1,7 @@
 .libs/
-./phpdbg
+phpdbg
 *.lo
 *.o
 build
+phpdbg_parser.c
+phpdbg_parser.h
index 5be6d5b00f4f3af44945fc74d001d29a26ad6f4e..45768de2effe542861128acda3975c5bd3b2cb70 100644 (file)
@@ -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
 
-
index 274e6409d04e488f7e39e3a2f6492b6ae3bcf2fe..3534d90124db20e94e0709747304f7b6e7b615e6 100644 (file)
--- a/config.m4
+++ b/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) \
index 29031507b31a7763c53c80a899b8a4153920d13c..fcc2b61a87ef970e0d354a86b965696df5cea5a6 100644 (file)
@@ -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");
 }
-
-
index 51a328d0b531a14e65aba00322b2fcb3dbc5bdb8..064e266082e724ac9f95cc47f8c694162fc290ec 100644 (file)
--- a/phpdbg.c
+++ b/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
 
index 68c505ca6367e7dcc7983a5165aa015994bf2bef..be009e40d042221f44e6a82d4d21979b56f20477 100644 (file)
--- a/phpdbg.h
+++ b/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)
 #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)
 #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"
 /* }}} */
 
 #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 */
index 511d1db57d79e0ff26f5671adc3350384522c0f4..a18316a2285e172aeec42e9630e8bbd158f4f8ef 100644 (file)
@@ -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);
index b1a9ddf4749697f12b2411f8d20733cbb456e6b8..97980e7ed78c215abed739c4262979ba2ca9db9c 100644 (file)
@@ -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); /* }}} */
index f56f76facd90dd0a76c6aef2bcf1387ba12960a2..be76b22b05965b03e5657174e6f5f2c136d085ff 100644 (file)
 #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;
 } /* }}} */
index f90e351d6de2e1b467a950f9da3a27c96b11db48..dc06da62b7976765f94dde9608b2d5b1ed97e85d 100644 (file)
 /**
  * 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/phpdbg_btree.c b/phpdbg_btree.c
new file mode 100644 (file)
index 0000000..8fc2561
--- /dev/null
@@ -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/phpdbg_btree.h b/phpdbg_btree.h
new file mode 100644 (file)
index 0000000..5fb217d
--- /dev/null
@@ -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
index 1d78c533216afc754a41e9013be197814329c8e2..d4ce8ebc55f36b4c28ab478a6b441eb2b1e54052 100644 (file)
 #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);
 } /* }}} */
 
index c86f92bb4787fec36ee61c7be365925f5d901048..571d065f59dd049282ad237669619dd4d4ad73fb 100644 (file)
@@ -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
index de02addc1b9f69572dfd2b7405528e4866fa74d7..a235fe8cb04c25a5ed46a2fdb4905a8b54830ccc 100644 (file)
@@ -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;
                }
 
index d2fea8d7c7a738adefbe27db06fd15900411fd7d..f2d074ded60c642d5cf31244d3395ebb4f38fc14 100644 (file)
    | 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 */}
+};  /* }}} */
index 319142cb5b200a03c8771fc71aedefc189f502db..16a1e771e3719b519638656c42e32b4429179932 100644 (file)
 /**
  * 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 */
index 0f4233bf30c7bff03ff93cf7f2482f4395e56748..97f88bfa1ec59437c6829dfb0f28323195a36df2 100644 (file)
 #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);
index a6b4e3719febd2e5e66598064737d6970a4d26e8..c36e6bebd65f6996260334abee2f9755efc3493d 100644 (file)
@@ -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/phpdbg_lexer.c b/phpdbg_lexer.c
new file mode 100644 (file)
index 0000000..c289004
--- /dev/null
@@ -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/phpdbg_lexer.h b/phpdbg_lexer.h
new file mode 100644 (file)
index 0000000..1958cef
--- /dev/null
@@ -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/phpdbg_lexer.l b/phpdbg_lexer.l
new file mode 100644 (file)
index 0000000..ad5edd9
--- /dev/null
@@ -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 */ }
+%%
index eb1091550b018d47208bca2772866a560b3c6f9d..037c6c38b2a1d749b9210a6aa127109841fa83f2 100644 (file)
 #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) /* {{{ */
index f9d1885eaf242c0ba1dd1c141e1707501b1bb479..14905f65673b7123fa9ef1d2c33710ec127d0976 100644 (file)
@@ -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 */
index 50073eb22bbdb3f7564f79490ddedaa2580c2e58..6b13625fc1417a4e0d8639c6edcdccb736d709ed 100644 (file)
@@ -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/phpdbg_parser.y b/phpdbg_parser.y
new file mode 100644 (file)
index 0000000..4a84504
--- /dev/null
@@ -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; }
+       ;
+
+%%
index c4925fe5fd78984c82c3d429b6ececf812aa1351..76321a5042d90fdedbbabaa0bfced6d9851c3e0b 100644 (file)
 
 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;
 } /* }}} */
index 80010d5e7cc8ef5ff2f160b8eedb0d468c9ffd28..ed85e965c1b428278d5281fd36850e82f24eea59 100644 (file)
@@ -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 */
index f586bb5a84f5ada451ce017b58e42f39061a5c77..7a28a75129fe962c6b646011d7c0b646d6d380d3 100644 (file)
 #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) {
index 6807d88f41d1659935c008c62e36fe5e2f5bc139..ef648aabeb08ad011979fb7673571eb35d567dd7 100644 (file)
@@ -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[]; /* }}} */
index 7c4da12a46e8c066f92c95193aa894f48052395e..54269a81931d63e421cdc06ebf5a9c5a21e30552 100644 (file)
 #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;
+} /* }}} */
index 120aeb34f4e210ac9f1181219dc06837fe317198..dea61ed3825b5fed020f072495315517b9673c67 100644 (file)
@@ -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 */
index 1effcfccaf93f7c90bc9a357efeb3297ccc9703e..c9b22a20397172b152ce74d834e3fb74fceaedac 100644 (file)
@@ -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;
+} /* }}} */
index c5164c3ac31490859fa6c368ce6d3a40ca3df784..68ae7e44a39257b60cec87839b0137a232a98d53 100644 (file)
@@ -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/phpdbg_watch.c b/phpdbg_watch.c
new file mode 100644 (file)
index 0000000..a6bf628
--- /dev/null
@@ -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/phpdbg_watch.h b/phpdbg_watch.h
new file mode 100644 (file)
index 0000000..d00bcff
--- /dev/null
@@ -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/phpdbg_win.c b/phpdbg_win.c
new file mode 100644 (file)
index 0000000..b0cbdf2
--- /dev/null
@@ -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/phpdbg_win.h b/phpdbg_win.h
new file mode 100644 (file)
index 0000000..68c3052
--- /dev/null
@@ -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
index 5fdbcbe1a4544453a9d2dd99abe2ea2effb14bb7..d93c81a89a42eee826f42e66c5305f5b853c91ae 100644 (file)
--- a/test.php
+++ b/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);