From: Bob Weinand Date: Mon, 21 Apr 2014 21:29:25 +0000 (+0200) Subject: Merge sapi/phpdbg into PHP-5.6 X-Git-Tag: php-5.6.3RC1~20^2~2^2~35 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3e3dcde8d7337b981354e69216a0cbf3950e2703;p=php Merge sapi/phpdbg into PHP-5.6 --- diff --git a/.gitignore b/.gitignore index 297efcbc42..af445861ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .libs/ -./phpdbg +phpdbg *.lo *.o build +phpdbg_parser.c +phpdbg_parser.h diff --git a/Makefile.frag b/Makefile.frag index 5be6d5b00f..45768de2ef 100644 --- a/Makefile.frag +++ b/Makefile.frag @@ -8,6 +8,15 @@ $(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS) $(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS) $(BUILD_PHPDBG) +$(builddir)/sapi/phpdbg/phpdbg_lexer.lo: $(srcdir)/sapi/phpdbg/phpdbg_parser.h + +$(srcdir)/sapi/phpdbg/phpdbg_lexer.c: $(srcdir)/sapi/phpdbg/phpdbg_lexer.l + @(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date -cbdFo sapi/phpdbg/phpdbg_lexer.c sapi/phpdbg/phpdbg_lexer.l) + +$(srcdir)/sapi/phpdbg/phpdbg_parser.h: $(srcdir)/sapi/phpdbg/phpdbg_parser.c +$(srcdir)/sapi/phpdbg/phpdbg_parser.c: $(srcdir)/sapi/phpdbg/phpdbg_parser.y + @$(YACC) -p phpdbg_ -v -d $(srcdir)/sapi/phpdbg/phpdbg_parser.y -o $@ + install-phpdbg: $(BUILD_BINARY) @echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/" @$(mkinstalldirs) $(INSTALL_ROOT)$(bindir) @@ -25,4 +34,3 @@ test-phpdbg: .PHONY: clean-phpdbg test-phpdbg - diff --git a/config.m4 b/config.m4 index 274e6409d0..3534d90124 100644 --- 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) \ diff --git a/config.w32 b/config.w32 index 29031507b3..fcc2b61a87 100644 --- a/config.w32 +++ b/config.w32 @@ -1,19 +1,18 @@ -ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes'); +ARG_ENABLE('phpdbg', 'Build phpdbg', 'no'); ARG_ENABLE('phpdbgs', 'Build phpdbg shared', 'no'); -PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c'; +PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_win.c phpdbg_btree.c'; PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll'; PHPDBG_EXE='phpdbg.exe'; if (PHP_PHPDBG == "yes") { - /* build phpdbg binary */ SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE); ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib"); + DEFINE("CFLAGS", configure_subst.item("CFLAGS") + " /EHa"); } if (PHP_PHPDBGS == "yes") { SAPI('phpdbgs', PHPDBG_SOURCES, PHPDBG_DLL, '/D PHP_PHPDBG_EXPORTS /I win32'); ADD_FLAG("LIBS_PHPDBGS", "ws2_32.lib user32.lib"); + DEFINE("CFLAGS", configure_subst.item("CFLAGS") + " /EHa"); } - - diff --git a/phpdbg.c b/phpdbg.c index 51a328d0b5..064e266082 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -#ifndef ZEND_SIGNALS +#if !defined(ZEND_SIGNALS) || defined(_WIN32) # include #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, ¶m TSRMLS_CC); - - switch (type) { - case METHOD_PARAM: - phpdbg_do_break_method(¶m, NULL TSRMLS_CC); - break; - - case FILE_PARAM: - phpdbg_do_break_file(¶m, NULL TSRMLS_CC); - break; - - case NUMERIC_PARAM: - phpdbg_do_break_lineno(¶m, NULL TSRMLS_CC); - break; - - case STR_PARAM: - phpdbg_do_break_func(¶m, NULL TSRMLS_CC); - break; - - default: zend_error( - E_WARNING, "unrecognized parameter type %ld", type); - } - + phpdbg_do_break(¶m TSRMLS_CC); phpdbg_clear_param(¶m TSRMLS_CC); } else if (EG(current_execute_data) && EG(active_op_array)) { @@ -319,9 +308,9 @@ static PHP_FUNCTION(phpdbg_clear) /* {{{ proto void phpdbg_color(integer element, string color) */ static PHP_FUNCTION(phpdbg_color) { - long element; - char *color; - zend_uint color_len; + long element = 0L; + char *color = NULL; + int color_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) { return; @@ -341,8 +330,8 @@ static PHP_FUNCTION(phpdbg_color) /* {{{ proto void phpdbg_prompt(string prompt) */ static PHP_FUNCTION(phpdbg_prompt) { - char *prompt; - zend_uint prompt_len; + char *prompt = NULL; + int prompt_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) { return; @@ -403,9 +392,9 @@ static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) { return FAILURE; } - + phpdbg_booted=1; - + return SUCCESS; } /* }}} */ @@ -585,7 +574,6 @@ const opt_struct OPTIONS[] = { /* {{{ */ {'z', 1, "load zend_extension"}, /* phpdbg options */ {'q', 0, "no banner"}, - {'e', 1, "exec"}, {'v', 0, "disable quietness"}, {'s', 0, "enable stepping"}, {'b', 0, "boring colours"}, @@ -615,10 +603,10 @@ const char phpdbg_ini_hardcoded[] = /* overwriteable ini defaults must be set in phpdbg_ini_defaults() */ #define INI_DEFAULT(name, value) \ - Z_SET_REFCOUNT(tmp, 0); \ - Z_UNSET_ISREF(tmp); \ - ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \ - zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL); + Z_SET_REFCOUNT(tmp, 0); \ + Z_UNSET_ISREF(tmp); \ + ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \ + zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL); void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ { @@ -666,11 +654,11 @@ static inline void phpdbg_sigint_handler(int signo) /* {{{ */ int phpdbg_open_socket(const char *interface, short port) /* {{{ */ { int fd = socket(AF_INET, SOCK_STREAM, 0); - + switch (fd) { case -1: return -1; - + default: { int reuse = 1; @@ -678,27 +666,27 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */ case -1: close(fd); return -2; - + default: { struct sockaddr_in address; - + memset(&address, 0, sizeof(address)); - + address.sin_port = htons(port); address.sin_family = AF_INET; - + if ((*interface == '*')) { - address.sin_addr.s_addr = htonl(INADDR_ANY); + address.sin_addr.s_addr = htonl(INADDR_ANY); } else if (!inet_pton(AF_INET, interface, &address.sin_addr)) { close(fd); return -3; } - + switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) { case -1: close(fd); return -4; - + default: { listen(fd, 5); } @@ -707,28 +695,28 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */ } } } - + return fd; } /* }}} */ static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */ -{ +{ if ((*socket)[0] >= 0) { shutdown( (*socket)[0], SHUT_RDWR); close((*socket)[0]); } - + if (streams[0]) { fclose(streams[0]); } - + if ((*socket)[1] >= 0) { shutdown( (*socket)[1], SHUT_RDWR); close((*socket)[1]); } - + if (streams[1]) { fclose(streams[1]); } @@ -798,14 +786,34 @@ int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*sock dup2((*socket)[0], fileno(stdin)); dup2((*socket)[1], fileno(stdout)); - + setbuf(stdout, NULL); streams[0] = fdopen((*socket)[0], "r"); streams[1] = fdopen((*socket)[1], "w"); - + return SUCCESS; } /* }}} */ + +void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) { + int is_handled = FAILURE; + TSRMLS_FETCH(); + + switch (sig) { + case SIGBUS: + case SIGSEGV: + is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC); + if (is_handled == FAILURE) { +#ifdef ZEND_SIGNALS + zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL TSRMLS_CC); +#else + sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL); +#endif + } + break; + } + +} #endif int main(int argc, char **argv) /* {{{ */ @@ -852,6 +860,10 @@ int main(int argc, char **argv) /* {{{ */ #endif #ifndef _WIN32 + struct sigaction signal_struct; + signal_struct.sa_sigaction = phpdbg_signal_handler; + signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER; + address = strdup("127.0.0.1"); socket[0] = -1; socket[1] = -1; @@ -888,17 +900,17 @@ phpdbg_main: bp_tmp_file = NULL; } } + + if (!bp_tmp_file) { + phpdbg_error("Unable to create temporary file"); + return 1; + } #else if (!mkstemp(bp_tmp_file)) { memset(bp_tmp_file, 0, sizeof(bp_tmp_file)); } #endif - if (!bp_tmp_file) { - phpdbg_error( - "Unable to create temporary file"); - return 1; - } } ini_entries = NULL; ini_entries_len = 0; @@ -920,8 +932,7 @@ phpdbg_main: run = 0; step = 0; sapi_name = NULL; - - + while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { switch (opt) { case 'r': @@ -965,7 +976,7 @@ phpdbg_main: ini_entries_len += len + sizeof("=1\n\0") - 2; } } break; - + case 'z': zend_extensions_len++; if (zend_extensions) { @@ -976,16 +987,6 @@ phpdbg_main: /* begin phpdbg options */ - case 'e': { /* set execution context */ - exec_len = strlen(php_optarg); - if (exec_len) { - if (exec) { - free(exec); - } - exec = strdup(php_optarg); - } - } break; - case 'S': { /* set SAPI name */ if (sapi_name) { free(sapi_name); @@ -1001,7 +1002,7 @@ phpdbg_main: if (init_file) { free(init_file); } - + init_file_len = strlen(php_optarg); if (init_file_len) { init_file = strdup(php_optarg); @@ -1038,7 +1039,7 @@ phpdbg_main: #ifndef _WIN32 /* if you pass a listen port, we will accept input on listen port */ /* and write output to listen port * 2 */ - + case 'l': { /* set listen ports */ if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) { if (sscanf(php_optarg, "%d", &listen[0]) != 1) { @@ -1050,7 +1051,7 @@ phpdbg_main: } } } break; - + case 'a': { /* set bind address */ free(address); if (!php_optarg) { @@ -1076,6 +1077,19 @@ phpdbg_main: } break; } } + + /* set exec if present on command line */ + if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS)) + { + exec_len = strlen(argv[php_optind]); + if (exec_len) { + if (exec) { + free(exec); + } + exec = strdup(argv[php_optind]); + } + php_optind++; + } #ifndef _WIN32 /* setup remote server if necessary */ @@ -1093,7 +1107,7 @@ phpdbg_main: if (sapi_name) { phpdbg->name = sapi_name; } - + phpdbg->ini_defaults = phpdbg_ini_defaults; phpdbg->phpinfo_as_text = 1; phpdbg->php_ini_ignore_cwd = 1; @@ -1114,14 +1128,14 @@ phpdbg_main: memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded)); } ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2; - + if (zend_extensions_len) { zend_ulong zend_extension = 0L; - + while (zend_extension < zend_extensions_len) { const char *ze = zend_extensions[zend_extension]; size_t ze_len = strlen(ze); - + ini_entries = realloc( ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n")))); memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1)); @@ -1133,40 +1147,81 @@ phpdbg_main: free(zend_extensions[zend_extension]); zend_extension++; } - + free(zend_extensions); } phpdbg->ini_entries = ini_entries; - + if (phpdbg->startup(phpdbg) == SUCCESS) { - php_request_startup(TSRMLS_C); +#ifdef _WIN32 + EXCEPTION_POINTERS *xp; + __try { +#endif + zend_mm_heap *mm_heap = zend_mm_set_heap(NULL TSRMLS_CC); +#if ZEND_DEBUG + if (!mm_heap->use_zend_alloc) { + mm_heap->_malloc = malloc; + mm_heap->_realloc = realloc; + mm_heap->_free = free; +#endif + PHPDBG_G(original_free_function) = mm_heap->_free; + mm_heap->_free = phpdbg_watch_efree; + mm_heap->use_zend_alloc = 0; +#if ZEND_DEBUG + } +#endif + zend_mm_set_heap(mm_heap TSRMLS_CC); + + zend_activate(TSRMLS_C); + +#if defined(ZEND_SIGNALS) && !defined(_WIN32) + zend_try { + zend_signal_activate(TSRMLS_C); + } zend_end_try(); +#endif + +#if defined(ZEND_SIGNALS) && !defined(_WIN32) + zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try(); + zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try(); +#elif !defined(_WIN32) + sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); + sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); +#endif + + if (php_request_startup(TSRMLS_C) == SUCCESS) { + int i; + + SG(request_info).argc = argc - php_optind + 1; + SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *)); + for (i = SG(request_info).argc; --i;) { + SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]); + } + SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup(""); + + php_hash_environment(TSRMLS_C); + } + + /* make sure to turn off buffer for ev command */ + php_output_activate(TSRMLS_C); + php_output_deactivate(TSRMLS_C); /* do not install sigint handlers for remote consoles */ /* sending SIGINT then provides a decent way of shutting down the server */ -#ifdef ZEND_SIGNALS -# ifndef _WIN32 +#if defined(ZEND_SIGNALS) && !defined(_WIN32) if (listen[0] < 0) { -# endif - zend_try { - zend_signal_activate(TSRMLS_C); - zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); - } zend_end_try(); -# ifndef _WIN32 + zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try(); } -# endif -#else -# ifndef _WIN32 +#elif !defined(_WIN32) if (listen[0] < 0) { -# endif +#endif signal(SIGINT, phpdbg_sigint_handler); #ifndef _WIN32 } -#endif #endif PG(modules_activated) = 0; - + /* set flags from command line */ PHPDBG_G(flags) = flags; @@ -1182,10 +1237,9 @@ phpdbg_main: PHPDBG_G(io)[PHPDBG_STDIN] = stdin; PHPDBG_G(io)[PHPDBG_STDOUT] = stdout; PHPDBG_G(io)[PHPDBG_STDERR] = stderr; - + if (exec) { /* set execution context */ - PHPDBG_G(exec) = phpdbg_resolve_path( - exec TSRMLS_CC); + PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC); PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec)); free(exec); @@ -1213,6 +1267,11 @@ phpdbg_main: phpdbg_welcome((cleaning > 0) TSRMLS_CC); } + /* auto compile */ + if (PHPDBG_G(exec)) { + phpdbg_compile(TSRMLS_C); + } + /* initialize from file */ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; zend_try { @@ -1233,14 +1292,17 @@ phpdbg_main: if (run) { /* no need to try{}, run does it ... */ - PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC); + PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC); if (run > 1) { /* if -r is on the command line more than once just quit */ goto phpdbg_out; } } +/* #ifndef for making compiler shutting up */ +#ifndef _WIN32 phpdbg_interact: +#endif /* phpdbg main() */ do { zend_try { @@ -1282,21 +1344,42 @@ phpdbg_interact: } zend_end_try(); } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); -phpdbg_out: + /* this must be forced */ + CG(unclean_shutdown) = 0; + + /* this is just helpful */ + PG(report_memleaks) = 0; + #ifndef _WIN32 +phpdbg_out: if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; goto phpdbg_interact; } #endif +#ifdef _WIN32 + } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) { + phpdbg_error("Access violation (Segementation fault) encountered\ntrying to abort cleanly..."); + } +phpdbg_out: +#endif + + { + int i; + /* free argv */ + for (i = SG(request_info).argc; --i;) { + efree(SG(request_info).argv[i]); + } + efree(SG(request_info).argv); + } + #ifndef ZTS /* force cleanup of auto and core globals */ zend_hash_clean(CG(auto_globals)); memset( &core_globals, 0, sizeof(php_core_globals)); #endif - if (ini_entries) { free(ini_entries); } @@ -1318,6 +1401,7 @@ phpdbg_out: } zend_end_try(); sapi_shutdown(); + } if (cleaning || remote) { @@ -1331,7 +1415,7 @@ phpdbg_out: #ifndef _WIN32 if (address) { - free(address); + free(address); } #endif diff --git a/phpdbg.h b/phpdbg.h index 68c505ca63..be009e40d0 100644 --- 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 #include @@ -68,6 +71,8 @@ #include "phpdbg_cmd.h" #include "phpdbg_utils.h" +#include "phpdbg_btree.h" +#include "phpdbg_watch.h" #ifdef ZTS # define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v) @@ -116,23 +121,26 @@ #define PHPDBG_IN_EVAL (1<<11) #define PHPDBG_IS_STEPPING (1<<12) -#define PHPDBG_IS_QUIET (1<<13) -#define PHPDBG_IS_QUITTING (1<<14) -#define PHPDBG_IS_COLOURED (1<<15) -#define PHPDBG_IS_CLEANING (1<<16) - -#define PHPDBG_IN_UNTIL (1<<17) -#define PHPDBG_IN_FINISH (1<<18) -#define PHPDBG_IN_LEAVE (1<<19) - -#define PHPDBG_IS_REGISTERED (1<<20) -#define PHPDBG_IS_STEPONEVAL (1<<21) -#define PHPDBG_IS_INITIALIZING (1<<22) -#define PHPDBG_IS_SIGNALED (1<<23) -#define PHPDBG_IS_INTERACTIVE (1<<24) -#define PHPDBG_IS_BP_ENABLED (1<<25) -#define PHPDBG_IS_REMOTE (1<<26) -#define PHPDBG_IS_DISCONNECTED (1<<27) +#define PHPDBG_STEP_OPCODE (1<<13) +#define PHPDBG_IS_QUIET (1<<14) +#define PHPDBG_IS_QUITTING (1<<15) +#define PHPDBG_IS_COLOURED (1<<16) +#define PHPDBG_IS_CLEANING (1<<17) + +#define PHPDBG_IN_UNTIL (1<<18) +#define PHPDBG_IN_FINISH (1<<19) +#define PHPDBG_IN_LEAVE (1<<20) + +#define PHPDBG_IS_REGISTERED (1<<21) +#define PHPDBG_IS_STEPONEVAL (1<<22) +#define PHPDBG_IS_INITIALIZING (1<<23) +#define PHPDBG_IS_SIGNALED (1<<24) +#define PHPDBG_IS_INTERACTIVE (1<<25) +#define PHPDBG_IS_BP_ENABLED (1<<26) +#define PHPDBG_IS_REMOTE (1<<27) +#define PHPDBG_IS_DISCONNECTED (1<<28) + +#define PHPDBG_SHOW_REFCOUNTS (1<<29) #define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE) #define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) @@ -149,7 +157,7 @@ #define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */ #define PHPDBG_URL "http://phpdbg.com" #define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues" -#define PHPDBG_VERSION "0.3.2" +#define PHPDBG_VERSION "0.4.0" #define PHPDBG_INIT_FILENAME ".phpdbginit" /* }}} */ @@ -159,12 +167,25 @@ #define PHPDBG_STDERR 2 #define PHPDBG_IO_FDS 3 /* }}} */ + /* {{{ structs */ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */ HashTable registered; /* registered */ HashTable seek; /* seek oplines */ phpdbg_frame_t frame; /* frame */ + zend_uint last_line; /* last executed line */ + +#ifndef _WIN32 + struct sigaction old_sigsegv_signal; /* segv signal handler */ +#endif + + phpdbg_btree watchpoint_tree; /* tree with watchpoints */ + phpdbg_btree watch_HashTables; /* tree with original dtors of watchpoints */ + HashTable watchpoints; /* watchpoints */ + zend_llist watchlist_mem; /* triggered watchpoints */ + zend_bool watchpoint_hit; /* a watchpoint was hit */ + void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */ char *exec; /* file to execute */ size_t exec_len; /* size of exec */ @@ -178,11 +199,24 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) char *prompt[2]; /* prompt */ const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */ - - phpdbg_command_t *lcmd; /* last command */ - phpdbg_param_t lparam; /* last param */ + char *buffer; /* buffer */ zend_ulong flags; /* phpdbg flags */ ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */ +/* the beginning (= the important part) of the _zend_mm_heap struct defined in Zend/zend_alloc.c + Needed for realizing watchpoints */ +struct _zend_mm_heap { + int use_zend_alloc; + void *(*_malloc)(size_t); + void (*_free)(void *); + void *(*_realloc)(void *, size_t); + size_t free_bitmap; + size_t large_free_bitmap; + size_t block_size; + size_t compact_size; + zend_mm_segment *segments_list; + zend_mm_storage *storage; +}; + #endif /* PHPDBG_H */ diff --git a/phpdbg_bp.c b/phpdbg_bp.c index 511d1db57d..a18316a228 100644 --- a/phpdbg_bp.c +++ b/phpdbg_bp.c @@ -135,27 +135,27 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */ switch (brake->type) { case PHPDBG_BREAK_FILE: { fprintf(handle, - "break file %s:%lu\n", + "break %s:%lu\n", ((phpdbg_breakfile_t*)brake)->filename, ((phpdbg_breakfile_t*)brake)->line); } break; case PHPDBG_BREAK_SYM: { fprintf(handle, - "break func %s\n", + "break %s\n", ((phpdbg_breaksymbol_t*)brake)->symbol); } break; case PHPDBG_BREAK_METHOD: { fprintf(handle, - "break method %s::%s\n", + "break %s::%s\n", ((phpdbg_breakmethod_t*)brake)->class_name, ((phpdbg_breakmethod_t*)brake)->func_name); } break; case PHPDBG_BREAK_METHOD_OPLINE: { fprintf(handle, - "break address %s::%s#%ld\n", + "break %s::%s#%ld\n", ((phpdbg_breakopline_t*)brake)->class_name, ((phpdbg_breakopline_t*)brake)->func_name, ((phpdbg_breakopline_t*)brake)->opline_num); @@ -163,21 +163,21 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */ case PHPDBG_BREAK_FUNCTION_OPLINE: { fprintf(handle, - "break address %s#%ld\n", + "break %s#%ld\n", ((phpdbg_breakopline_t*)brake)->func_name, ((phpdbg_breakopline_t*)brake)->opline_num); } break; case PHPDBG_BREAK_FILE_OPLINE: { fprintf(handle, - "break address %s:%ld\n", + "break %s:#%ld\n", ((phpdbg_breakopline_t*)brake)->class_name, ((phpdbg_breakopline_t*)brake)->opline_num); } break; case PHPDBG_BREAK_OPCODE: { fprintf(handle, - "break op %s\n", + "break %s\n", ((phpdbg_breakop_t*)brake)->name); } break; @@ -209,7 +209,7 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */ } } else { fprintf( - handle, "break on %s\n", conditional->code); + handle, "break if %s\n", conditional->code); } } break; } @@ -221,14 +221,20 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */ PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */ { - struct stat sb; - - if (VCWD_STAT(path, &sb) != FAILURE) { - if (sb.st_mode & (S_IFREG|S_IFLNK)) { + php_stream_statbuf ssb; + char realpath[MAXPATHLEN]; + + if (php_stream_stat_path(path, &ssb) != FAILURE) { + if (ssb.sb.st_mode & (S_IFREG|S_IFLNK)) { HashTable *broken; phpdbg_breakfile_t new_break; - size_t path_len = strlen(path); - + size_t path_len = 0L; + + if (VCWD_REALPATH(path, realpath)) { + path = realpath; + } + path_len = strlen(path); + if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], path, path_len, (void**)&broken) == FAILURE) { HashTable breaks; @@ -324,9 +330,9 @@ PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char PHPDBG_BREAK_MAPPING(new_break.id, class_table); } else { phpdbg_notice("Breakpoint exists at %s::%s", class_name, func_name); - } + } - efree(lcname); + efree(lcname); } /* }}} */ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */ @@ -355,7 +361,7 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC) /* {{{ */ { phpdbg_breakline_t opline_break; - if (op_array->last < brake->opline_num) { + if (op_array->last <= brake->opline_num) { if (brake->class_name == NULL) { phpdbg_error("There are only %d oplines in function %s (breaking at opline %ld impossible)", op_array->last, brake->func_name, brake->opline_num); } else if (brake->func_name == NULL) { @@ -760,56 +766,26 @@ PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_l } } /* }}} */ -PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ { - if (input->argc > 3 && phpdbg_argv_is(2, "if")) { - phpdbg_breakcond_t new_break; - phpdbg_param_t new_param; - - zend_ulong expr_hash = 0L; - size_t expr_len; - const char *join = strstr(input->string, "if"); - const char *expr = (join) + sizeof("if"); - - expr_len = strlen(expr); - expr = phpdbg_trim(expr, expr_len, &expr_len); - expr_hash = zend_inline_hash_func(expr, expr_len); - - { - /* get a clean parameter from input string */ - size_t sparam_len = 0L; - char *sparam = input->string; - - sparam[ - strstr(input->string, " ") - input->string] = 0; - sparam_len = strlen(sparam); - - switch (phpdbg_parse_param(sparam, sparam_len, &new_param TSRMLS_CC)) { - case EMPTY_PARAM: - case NUMERIC_PARAM: - phpdbg_clear_param( - &new_param TSRMLS_CC); - goto usage; - - default: { /* do nothing */ } break; - } - - expr_hash += phpdbg_hash_param(&new_param TSRMLS_CC); - } - - if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) { + phpdbg_breakcond_t new_break; + phpdbg_param_t *condition; + zend_ulong hash = 0L; + + if (param->next) { + condition = param->next; + hash = zend_inline_hash_func(condition->str, condition->len); + + if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) { phpdbg_create_conditional_break( - &new_break, &new_param, expr, expr_len, expr_hash TSRMLS_CC); + &new_break, param, + condition->str, condition->len, hash TSRMLS_CC); } else { phpdbg_notice( - "Conditional break %s exists at the specified location", expr); - } - - phpdbg_clear_param(&new_param TSRMLS_CC); - } else { -usage: - phpdbg_error("usage: break at if "); + "Conditional break %s exists at the specified location", condition->str); + } } + } /* }}} */ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */ @@ -992,7 +968,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execut for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position); zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS; - zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) { + zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) { zval *retval = NULL; int orig_interactive = CG(interactive); zval **orig_retval = EG(return_value_ptr_ptr); diff --git a/phpdbg_bp.h b/phpdbg_bp.h index b1a9ddf474..97980e7ed7 100644 --- a/phpdbg_bp.h +++ b/phpdbg_bp.h @@ -119,7 +119,7 @@ PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const cha PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC); -PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */ +PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC); /* }}} */ /* {{{ Breakpoint Detection API */ PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */ diff --git a/phpdbg_break.c b/phpdbg_break.c index f56f76facd..be76b22b05 100644 --- a/phpdbg_break.c +++ b/phpdbg_break.c @@ -24,132 +24,32 @@ #include "phpdbg_opcode.h" #include "phpdbg_break.h" #include "phpdbg_bp.h" +#include "phpdbg_prompt.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -PHPDBG_BREAK(file) /* {{{ */ -{ - switch (param->type) { - case FILE_PARAM: - phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ - -PHPDBG_BREAK(method) /* {{{ */ -{ - switch (param->type) { - case METHOD_PARAM: - phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ - -PHPDBG_BREAK(address) /* {{{ */ -{ - switch (param->type) { - case ADDR_PARAM: - phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC); - break; - - case NUMERIC_METHOD_PARAM: - phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC); - break; - - case NUMERIC_FUNCTION_PARAM: - phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC); - break; +#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10]) - case FILE_PARAM: - phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ - -PHPDBG_BREAK(on) /* {{{ */ -{ - switch (param->type) { - case STR_PARAM: - phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ +/** + * Commands + */ +const phpdbg_command_t phpdbg_break_commands[] = { + PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c"), + PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n"), + PHPDBG_END_COMMAND +}; PHPDBG_BREAK(at) /* {{{ */ { - phpdbg_set_breakpoint_at(param, input TSRMLS_CC); - - return SUCCESS; -} /* }}} */ - -PHPDBG_BREAK(lineno) /* {{{ */ -{ - switch (param->type) { - case NUMERIC_PARAM: { - if (PHPDBG_G(exec)) { - phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC); - } else { - phpdbg_error("Execution context not set!"); - } - } break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ - -PHPDBG_BREAK(func) /* {{{ */ -{ - switch (param->type) { - case STR_PARAM: - phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ - -PHPDBG_BREAK(op) /* {{{ */ -{ - switch (param->type) { - case STR_PARAM: - phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } + phpdbg_set_breakpoint_at(param TSRMLS_CC); return SUCCESS; } /* }}} */ PHPDBG_BREAK(del) /* {{{ */ { - switch (param->type) { - case NUMERIC_PARAM: { - phpdbg_delete_breakpoint(param->num TSRMLS_CC); - } break; - - phpdbg_default_switch_case(); - } + phpdbg_delete_breakpoint(param->num TSRMLS_CC); return SUCCESS; } /* }}} */ diff --git a/phpdbg_break.h b/phpdbg_break.h index f90e351d6d..dc06da62b7 100644 --- a/phpdbg_break.h +++ b/phpdbg_break.h @@ -29,30 +29,9 @@ /** * Printer Forward Declarations */ -PHPDBG_BREAK(file); -PHPDBG_BREAK(func); -PHPDBG_BREAK(method); -PHPDBG_BREAK(address); PHPDBG_BREAK(at); -PHPDBG_BREAK(op); -PHPDBG_BREAK(on); -PHPDBG_BREAK(lineno); PHPDBG_BREAK(del); -/** - * Commands - */ -static const phpdbg_command_t phpdbg_break_commands[] = { - PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1), - PHPDBG_COMMAND_D_EX(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1), - PHPDBG_COMMAND_D_EX(method, "specify breakpoint by class::method", 'm', break_method, NULL, 1), - PHPDBG_COMMAND_D_EX(address, "specify breakpoint by address", 'a', break_address, NULL, 1), - PHPDBG_COMMAND_D_EX(op, "specify breakpoint by opcode", 'O', break_op, NULL, 1), - PHPDBG_COMMAND_D_EX(on, "specify breakpoint by condition", 'o', break_on, NULL, 1), - PHPDBG_COMMAND_D_EX(at, "specify breakpoint by location and condition", 'A', break_at, NULL, 1), - PHPDBG_COMMAND_D_EX(lineno, "specify breakpoint by line of currently executing file", 'l', break_lineno, NULL, 1), - PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1), - PHPDBG_END_COMMAND -}; +extern const phpdbg_command_t phpdbg_break_commands[]; #endif /* PHPDBG_BREAK_H */ diff --git a/phpdbg_btree.c b/phpdbg_btree.c new file mode 100644 index 0000000000..8fc2561e04 --- /dev/null +++ b/phpdbg_btree.c @@ -0,0 +1,221 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#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 index 0000000000..5fb217db35 --- /dev/null +++ b/phpdbg_btree.h @@ -0,0 +1,65 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHPDBG_BTREE_H +#define PHPDBG_BTREE_H + +#include "zend.h" + +typedef struct { + zend_ulong idx; + void *ptr; +} phpdbg_btree_result; + +typedef union _phpdbg_btree_branch phpdbg_btree_branch; +union _phpdbg_btree_branch { + phpdbg_btree_branch *branches[2]; + phpdbg_btree_result result; +}; + +typedef struct { + zend_ulong count; + zend_ulong depth; + phpdbg_btree_branch *branch; +} phpdbg_btree; + +typedef struct { + phpdbg_btree *tree; + zend_ulong cur; + zend_ulong end; +} phpdbg_btree_position; + +void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth); +phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx); +phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx); +phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx); +phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos); +int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx); + +#define PHPDBG_BTREE_INSERT 1 +#define PHPDBG_BTREE_UPDATE 2 +#define PHPDBG_BTREE_OVERWRITE (PHPDBG_BTREE_INSERT | PHPDBG_BTREE_UPDATE) + +int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags); +#define phpdbg_btree_insert(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_INSERT) +#define phpdbg_btree_update(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_UPDATE) +#define phpdbg_btree_overwrite(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_OWERWRITE) + +#endif diff --git a/phpdbg_cmd.c b/phpdbg_cmd.c index 1d78c53321..d4ce8ebc55 100644 --- a/phpdbg_cmd.c +++ b/phpdbg_cmd.c @@ -22,12 +22,32 @@ #include "phpdbg_cmd.h" #include "phpdbg_utils.h" #include "phpdbg_set.h" +#include "phpdbg_prompt.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); +static inline const char *phpdbg_command_name(const phpdbg_command_t *command, char *buffer) { + size_t pos = 0; + + if (command->parent) { + memcpy(&buffer[pos], command->parent->name, command->parent->name_len); + pos += command->parent->name_len; + memcpy(&buffer[pos], " ", sizeof(" ")-1); + pos += (sizeof(" ")-1); + } + + memcpy(&buffer[pos], command->name, command->name_len); + pos += command->name_len; + buffer[pos] = 0; + + return buffer; +} + PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ { switch (param->type) { + case STACK_PARAM: + return "stack"; case EMPTY_PARAM: return "empty"; case ADDR_PARAM: @@ -208,10 +228,19 @@ PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **point PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */ { switch ((dest->type = src->type)) { + case STACK_PARAM: + /* nope */ + break; + case STR_PARAM: dest->str = estrndup(src->str, src->len); dest->len = src->len; break; + + case OP_PARAM: + dest->str = estrndup(src->str, src->len); + dest->len = src->len; + break; case ADDR_PARAM: dest->addr = src->addr; @@ -226,6 +255,7 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des dest->method.name = estrdup(src->method.name); break; + case NUMERIC_FILE_PARAM: case FILE_PARAM: dest->file.name = estrdup(src->file.name); dest->file.line = src->file.line; @@ -246,6 +276,10 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des break; case EMPTY_PARAM: { /* do nothing */ } break; + + default: { + /* not yet */ + } } } /* }}} */ @@ -254,6 +288,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) / zend_ulong hash = param->type; switch (param->type) { + case STACK_PARAM: + /* nope */ + break; + case STR_PARAM: hash += zend_inline_hash_func(param->str, param->len); break; @@ -291,6 +329,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) / break; case EMPTY_PARAM: { /* do nothing */ } break; + + default: { + /* not yet */ + } } return hash; @@ -301,7 +343,11 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa if (l && r) { if (l->type == r->type) { switch (l->type) { - + case STACK_PARAM: + /* nope, or yep */ + return 1; + break; + case NUMERIC_FUNCTION_PARAM: if (l->num != r->num) { break; @@ -356,112 +402,400 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa case EMPTY_PARAM: return 1; + + default: { + /* not yet */ + } } } } return 0; } /* }}} */ -PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */ -{ - char *p; - char b[PHPDBG_MAX_CMD]; - int l=0; - enum states { - IN_BETWEEN, - IN_WORD, - IN_STRING - } state = IN_BETWEEN; - phpdbg_input_t **argv = NULL; - - argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*)); - (*argc) = 0; - -#define RESET_STATE() do { \ - phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \ - if (arg) { \ - b[l]=0; \ - arg->length = l; \ - arg->string = estrndup(b, arg->length); \ - arg->argv = NULL; \ - arg->argc = 0; \ - argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \ - argv[(*argc)++] = arg; \ - l = 0; \ - } \ - state = IN_BETWEEN; \ -} while (0) - - for (p = buffer; *p != '\0'; p++) { - int c = (unsigned char) *p; - switch (state) { - case IN_BETWEEN: - if (isspace(c)) { - continue; - } - if (c == '"') { - state = IN_STRING; - continue; - } - state = IN_WORD; - b[l++]=c; - continue; - - case IN_STRING: - if (c == '"') { - if (buffer[(p - buffer)-1] == '\\') { - b[l-1]=c; - continue; - } - RESET_STATE(); - } else { - b[l++]=c; - } - continue; +/* {{{ */ +PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) { + if (param && param->type) { + switch (param->type) { + case STR_PARAM: + fprintf(stderr, "%s STR_PARAM(%s=%lu)\n", msg, param->str, param->len); + break; + + case ADDR_PARAM: + fprintf(stderr, "%s ADDR_PARAM(%lu)\n", msg, param->addr); + break; + + case NUMERIC_FILE_PARAM: + fprintf(stderr, "%s NUMERIC_FILE_PARAM(%s:#%lu)\n", msg, param->file.name, param->file.line); + break; + + case FILE_PARAM: + fprintf(stderr, "%s FILE_PARAM(%s:%lu)\n", msg, param->file.name, param->file.line); + break; + + case METHOD_PARAM: + fprintf(stderr, "%s METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name); + break; + + case NUMERIC_METHOD_PARAM: + fprintf(stderr, "%s NUMERIC_METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name); + break; + + case NUMERIC_FUNCTION_PARAM: + fprintf(stderr, "%s NUMERIC_FUNCTION_PARAM(%s::%ld)\n", msg, param->str, param->num); + break; + + case NUMERIC_PARAM: + fprintf(stderr, "%s NUMERIC_PARAM(%ld)\n", msg, param->num); + break; + + case COND_PARAM: + fprintf(stderr, "%s COND_PARAM(%s=%lu)\n", msg, param->str, param->len); + break; + + case OP_PARAM: + fprintf(stderr, "%s OP_PARAM(%s=%lu)\n", msg, param->str, param->len); + break; + + default: { + /* not yet */ + } + } + } +} /* }}} */ - case IN_WORD: - if (isspace(c)) { - RESET_STATE(); - } else { - b[l++]=c; +/* {{{ */ +PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack) { + if (stack && stack->next) { + phpdbg_param_t *remove = stack->next; + + while (remove) { + phpdbg_param_t *next = NULL; + + if (remove->next) + next = remove->next; + + switch (remove->type) { + case NUMERIC_METHOD_PARAM: + case METHOD_PARAM: + if (remove->method.class) + free(remove->method.class); + if (remove->method.name) + free(remove->method.name); + break; + + case NUMERIC_FUNCTION_PARAM: + case STR_PARAM: + case OP_PARAM: + if (remove->str) + free(remove->str); + break; + + case NUMERIC_FILE_PARAM: + case FILE_PARAM: + if (remove->file.name) + free(remove->file.name); + break; + + default: { + /* nothing */ } - continue; + } + + free(remove); + remove = NULL; + + if (next) + remove = next; + else break; } } + + stack->next = NULL; +} /* }}} */ - switch (state) { - case IN_WORD: { - RESET_STATE(); - } break; +/* {{{ */ +PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) { + phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t)); - case IN_STRING: - phpdbg_error( - "Malformed command line (unclosed quote) @ %ld: %s!", - (p - buffer)-1, &buffer[(p - buffer)-1]); - break; + if (!next) + return; - case IN_BETWEEN: - break; + *(next) = *(param); + + next->next = NULL; + + if (stack->top == NULL) { + stack->top = next; + next->top = NULL; + stack->next = next; + } else { + stack->top->next = next; + next->top = stack->top; + stack->top = next; } - if ((*argc) == 0) { - /* not needed */ - efree(argv); + stack->len++; +} /* }}} */ - /* to be sure */ - return NULL; +PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC) { + if (command) { + char buffer[128] = {0,}; + const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL; + const char *arg = command->args; + size_t least = 0L, + received = 0L, + current = 0L; + zend_bool optional = 0; + + /* check for arg spec */ + if (!(arg) || !(*arg)) { + if (!top) { + return SUCCESS; + } + + asprintf(why, + "The command \"%s\" expected no arguments", + phpdbg_command_name(command, buffer)); + return FAILURE; + } + + least = 0L; + + /* count least amount of arguments */ + while (arg && *arg) { + if (arg[0] == '|') { + break; + } + least++; + arg++; + } + + arg = command->args; + +#define verify_arg(e, a, t) if (!(a)) { \ + if (!optional) { \ + asprintf(why, \ + "The command \"%s\" expected %s and got nothing at parameter %lu", \ + phpdbg_command_name(command, buffer), \ + (e), \ + current); \ + return FAILURE;\ + } \ +} else if ((a)->type != (t)) { \ + asprintf(why, \ + "The command \"%s\" expected %s and got %s at parameter %lu", \ + phpdbg_command_name(command, buffer), \ + (e),\ + phpdbg_get_param_type((a) TSRMLS_CC), \ + current); \ + return FAILURE; \ +} + + while (arg && *arg) { + current++; + + switch (*arg) { + case '|': { + current--; + optional = 1; + arg++; + } continue; + + case 'i': verify_arg("raw input", top, STR_PARAM); break; + case 's': verify_arg("string", top, STR_PARAM); break; + case 'n': verify_arg("number", top, NUMERIC_PARAM); break; + case 'm': verify_arg("method", top, METHOD_PARAM); break; + case 'a': verify_arg("address", top, ADDR_PARAM); break; + case 'f': verify_arg("file:line", top, FILE_PARAM); break; + case 'c': verify_arg("condition", top, COND_PARAM); break; + case 'o': verify_arg("opcode", top, OP_PARAM); break; + case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break; + + case '*': { /* do nothing */ } break; + } + + if (top ) { + top = top->next; + } else break; + + received++; + arg++; + } + +#undef verify_arg + + if ((received < least)) { + asprintf(why, + "The command \"%s\" expected at least %lu arguments (%s) and received %lu", + phpdbg_command_name(command, buffer), + least, + command->args, + received); + return FAILURE; + } } + + return SUCCESS; +} + +/* {{{ */ +PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why) { + const phpdbg_command_t *command = commands; + phpdbg_param_t *name = *top; + const phpdbg_command_t *matched[3] = {NULL, NULL, NULL}; + ulong matches = 0L; + + while (command && command->name && command->handler) { + if ((name->len == 1) || (command->name_len >= name->len)) { + /* match single letter alias */ + if (command->alias && (name->len == 1)) { + if (command->alias == (*name->str)) { + matched[matches] = command; + matches++; + } + } else { - return argv; + /* match full, case insensitive, command name */ + if (strncasecmp(command->name, name->str, name->len) == SUCCESS) { + if (matches < 3) { + + /* only allow abbreviating commands that can be aliased */ + if (((name->len != command->name_len) && command->alias) || + (name->len == command->name_len)) { + matched[matches] = command; + matches++; + } + + + /* exact match */ + if (name->len == command->name_len) + break; + } else break; + } + } + } + + command++; + } + + switch (matches) { + case 0: { + if (parent) { + asprintf( + why, + "The command \"%s %s\" could not be found", + parent->name, name->str); + } else asprintf( + why, + "The command \"%s\" could not be found", + name->str); + } return parent; + + case 1: { + (*top) = (*top)->next; + + command = matched[0]; + } break; + + default: { + char *list = NULL; + zend_uint it = 0; + size_t pos = 0; + + while (it < matches) { + if (!list) { + list = malloc( + matched[it]->name_len + 1 + + ((it+1) < matches ? sizeof(", ")-1 : 0)); + } else { + list = realloc(list, + (pos + matched[it]->name_len) + 1 + + ((it+1) < matches ? sizeof(", ")-1 : 0)); + } + memcpy(&list[pos], matched[it]->name, matched[it]->name_len); + pos += matched[it]->name_len; + if ((it+1) < matches) { + memcpy(&list[pos], ", ", sizeof(", ")-1); + pos += (sizeof(", ") - 1); + } + + list[pos] = 0; + it++; + } + + asprintf( + why, + "The command \"%s\" is ambigious, matching %lu commands (%s)", + name->str, matches, list); + free(list); + } return NULL; + } + + if (command->subs && (*top) && ((*top)->type == STR_PARAM)) { + return phpdbg_stack_resolve(command->subs, command, top, why); + } else { + return command; + } + + return NULL; } /* }}} */ -PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */ +/* {{{ */ +PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC) { + phpdbg_param_t *top = NULL; + const phpdbg_command_t *handler = NULL; + + if (stack->type != STACK_PARAM) { + asprintf( + why, "The passed argument was not a stack !!"); + return FAILURE; + } + + if (!stack->len) { + asprintf( + why, "The stack contains nothing !!"); + return FAILURE; + } + + top = (phpdbg_param_t*) stack->next; + + switch (top->type) { + case EVAL_PARAM: + return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC); + + case RUN_PARAM: + return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC); + + case SHELL_PARAM: + return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC); + + case STR_PARAM: { + handler = phpdbg_stack_resolve( + phpdbg_prompt_commands, NULL, &top, why); + + if (handler) { + if (phpdbg_stack_verify(handler, &top, why TSRMLS_CC) == SUCCESS) { + return handler->handler(top TSRMLS_CC); + } + } + } return FAILURE; + + default: + asprintf( + why, "The first parameter makes no sense !!"); + return FAILURE; + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */ { - phpdbg_input_t *buffer = NULL; char *cmd = NULL; #ifndef HAVE_LIBREADLINE char buf[PHPDBG_MAX_CMD]; #endif + char *buffer = NULL; if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && @@ -513,32 +847,8 @@ readline: } #endif } else cmd = buffered; - - /* allocate and sanitize buffer */ - buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t)); - if (!buffer) { - return NULL; - } - - buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length); - - /* store constant pointer to start of buffer */ - buffer->start = (char* const*) buffer->string; - - buffer->argv = phpdbg_read_argv( - buffer->string, &buffer->argc TSRMLS_CC); - -#ifdef PHPDBG_DEBUG - if (buffer->argc) { - int arg = 0; - - while (arg < buffer->argc) { - phpdbg_debug( - "argv %d=%s", arg, buffer->argv[arg]->string); - arg++; - } - } -#endif + + buffer = estrdup(cmd); #ifdef HAVE_LIBREADLINE if (!buffered && cmd && @@ -546,144 +856,34 @@ readline: free(cmd); } #endif - - return buffer; } - return NULL; -} /* }}} */ + if (buffer && isspace(*buffer)) { + char *trimmed = buffer; + while (isspace(*trimmed)) + trimmed++; -PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */ -{ - if (argv) { - if (argc) { - int arg; - for (arg=0; argstring) { - 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, - ¶m 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; argargc; 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(¶m, 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(¶m TSRMLS_CC); - } - break; - } - command++; - } - } else { - /* this should NEVER happen */ - phpdbg_error( - "No function executed!!"); - } - - return rc; + efree(*input); } /* }}} */ diff --git a/phpdbg_cmd.h b/phpdbg_cmd.h index c86f92bb47..571d065f59 100644 --- a/phpdbg_cmd.h +++ b/phpdbg_cmd.h @@ -23,8 +23,6 @@ #include "TSRM.h" -typedef struct _phpdbg_command_t phpdbg_command_t; - /* {{{ Command and Parameter */ enum { NO_ARG = 0, @@ -36,24 +34,23 @@ typedef enum { EMPTY_PARAM = 0, ADDR_PARAM, FILE_PARAM, + NUMERIC_FILE_PARAM, METHOD_PARAM, STR_PARAM, NUMERIC_PARAM, NUMERIC_FUNCTION_PARAM, - NUMERIC_METHOD_PARAM + NUMERIC_METHOD_PARAM, + STACK_PARAM, + EVAL_PARAM, + SHELL_PARAM, + COND_PARAM, + OP_PARAM, + ORIG_PARAM, + RUN_PARAM } phpdbg_param_type; -typedef struct _phpdbg_input_t phpdbg_input_t; - -struct _phpdbg_input_t { - char * const *start; - char *string; - size_t length; - phpdbg_input_t **argv; - int argc; -}; - -typedef struct _phpdbg_param { +typedef struct _phpdbg_param phpdbg_param_t; +struct _phpdbg_param { phpdbg_param_type type; long num; zend_ulong addr; @@ -67,10 +64,31 @@ typedef struct _phpdbg_param { } method; char *str; size_t len; -} phpdbg_param_t; + phpdbg_param_t *next; + phpdbg_param_t *top; +}; -typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t*, const phpdbg_input_t* TSRMLS_DC); +#define phpdbg_init_param(v, t) do{ \ + (v)->type = (t); \ + (v)->addr = 0; \ + (v)->num = 0; \ + (v)->file.name = NULL; \ + (v)->file.line = 0; \ + (v)->method.class = NULL; \ + (v)->method.name = NULL; \ + (v)->str = NULL; \ + (v)->len = 0; \ + (v)->next = NULL; \ + (v)->top = NULL; \ +} while(0) + +#ifndef YYSTYPE +#define YYSTYPE phpdbg_param_t +#endif + +typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t* TSRMLS_DC); +typedef struct _phpdbg_command_t phpdbg_command_t; struct _phpdbg_command_t { const char *name; /* Command name */ size_t name_len; /* Command name length */ @@ -79,7 +97,8 @@ struct _phpdbg_command_t { char alias; /* Alias */ phpdbg_command_handler_t handler; /* Command handler */ const phpdbg_command_t *subs; /* Sub Commands */ - char arg_type; /* Accept args? */ + char *args; /* Argument Spec */ + const phpdbg_command_t *parent; /* Parent Command */ }; /* }}} */ @@ -95,35 +114,29 @@ typedef struct { } phpdbg_frame_t; /* }}} */ - - /* * Workflow: -* 1) read input -* input takes the line from console, creates argc/argv -* 2) parse parameters into suitable types based on arg_type -* takes input from 1) and arg_type and creates parameters -* 3) do command -* executes commands -* 4) destroy parameters -* cleans up what was allocated by creation of parameters -* 5) destroy input -* cleans up what was allocated by creation of input +* 1) the lexer/parser creates a stack of commands and arguments from input +* 2) the commands at the top of the stack are resolved sensibly using aliases, abbreviations and case insensitive matching +* 3) the remaining arguments in the stack are verified (optionally) against the handlers declared argument specification +* 4) the handler is called passing the top of the stack as the only parameter +* 5) the stack is destroyed upon return from the handler */ /* * Input Management */ -PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC); -PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC); +PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC); +PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC); -/* -* Argument Management -*/ -PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC); -PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC); -#define phpdbg_argv_is(n, s) \ - (memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS) +/** + * Stack Management + */ +PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param); +PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why); +PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC); +PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC); +PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack); /* * Parameter Management @@ -135,28 +148,27 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_par PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC); PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC); PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC); - -/* -* Command Executor -*/ -PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t*, phpdbg_input_t* TSRMLS_DC); +PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg); /** * Command Declarators */ #define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name -#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, has_args) \ - {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, has_args} +#define PHPDBG_COMMAND_D_EXP(name, tip, alias, handler, children, args, parent) \ + {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent} + +#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args) \ + {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL} -#define PHPDBG_COMMAND_D(name, tip, alias, children, has_args) \ - {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, has_args} +#define PHPDBG_COMMAND_D(name, tip, alias, children, args) \ + {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL} -#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) +#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC) -#define PHPDBG_COMMAND_ARGS param, input TSRMLS_CC +#define PHPDBG_COMMAND_ARGS param TSRMLS_CC -#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0'} +#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL} /* * Default Switch Case diff --git a/phpdbg_frame.c b/phpdbg_frame.c index de02addc1b..a235fe8cb0 100644 --- a/phpdbg_frame.c +++ b/phpdbg_frame.c @@ -167,7 +167,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */ zval **tmp; zval **file, **line; HashPosition position; - int i = 1, limit = num; + int i = 0, limit = num; int user_defined; if (limit < 0) { @@ -186,7 +186,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */ if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position) == FAILURE) { - phpdbg_write("frame #0: {main} at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line)); + phpdbg_write("frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line)); break; } diff --git a/phpdbg_help.c b/phpdbg_help.c index d2fea8d7c7..f2d074ded6 100644 --- a/phpdbg_help.c +++ b/phpdbg_help.c @@ -15,589 +15,916 @@ | Authors: Felipe Pena | | Authors: Joe Watkins | | Authors: Bob Weinand | + | Authors: Terry Ellison | +----------------------------------------------------------------------+ */ #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 + * 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 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 ** 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 ** 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 " CR +" **colors** **C** set colors []" CR +" **oplog** **O** set oplog [output]" CR +" **break** **b** set break **id** " CR +" **breaks** **B** set breaks []" CR +" **quiet** **q** set quiet []" CR +" **stepping** **s** set stepping []" CR +" **refcount** **r** set refcount [] " CR CR + +"Valid colors are **none**, **white**, **red**, **green**, **yellow**, **blue**, **purple**, " +"**cyan** and **black**. All colours except **none** can be followed by an optional " +"**-bold** or **-underline** qualifier." CR CR + +"Color elements can be one of **prompt**, **notice**, or **error**." CR CR + +"**Examples**" CR CR +" $P S C on" CR +" Set colors on" CR CR + +" $P set p >" CR +" $P set color prompt white-bold" CR +" Set the prompt to a bold >" CR CR + +" $P S c error red-bold" CR +" Use red bold for errors" CR CR + +" $P S refcount on" CR +" Enable refcount display when hitting watchpoints" CR CR + +" $P S b 4 off" CR +" Temporarily disable breakpoint 4. This can be subsequently reenabled by a **s b 4 on**." CR +//*********** check oplog syntax +}, + +{"sh", +"Direct access to shell commands saves having to switch windows/consoles" CR CR + +"**Examples**" CR CR +" $P sh ls /usr/src/php-src" CR +" Will execute ls /usr/src/php-src, displaying the output in the console" +//*********** what does this mean????Note: read only commands please! +}, + +{"source", +"Sourcing a **phpdbginit** script during your debugging session might save some time." CR CR + +"**Examples**" CR CR + +" $P source /my/init" CR +" $P < /my/init" CR +" Will execute the phpdbginit file at /my/init" CR CR +}, + +{"export", +"Exporting breakpoints allows you to share, and or save your current debugging session" CR CR + +"**Examples**" CR CR + +" $P export /my/exports" CR +" $P > /my/exports" CR +" Will export all breakpoints to /my/exports" CR CR +}, + +{"step", +"Execute opcodes until next line" CR CR + +"**Examples**" CR CR + +" $P s" CR +" Will continue and break again in the next encountered line" CR CR +}, + +{"until", +"The **until** command causes control to be passed back to the vm, continuing execution. Any " +"breakpoints that are encountered before the next source line will be skipped. Execution " +"will then continue until the next breakpoint or completion of the script" CR CR + +"Note when **step**ping is enabled, any opcode steps within the current line are also skipped. "CR CR + +"Note that if the next line is **not** executed then **all** subsequent breakpoints will be " +"skipped. " CR CR + +"Note **until** will trigger a \"not executing\" error if not executing." + +}, +{"watch", +"Sets watchpoints on variables as long as they are defined" CR +"Passing no parameter to **watch**, lists all actually active watchpoints" CR CR + +"**Format for $variable**" CR CR +" **$var** Variable $var" CR +" **$var[]** All array elements of $var" CR +" **$var->** All properties of $var" CR +" **$var->a** Property $var->a" CR +" **$var[b]** Array element with key b in array $var" CR CR + +"Subcommands of **watch**:" CR CR + +" **Type** **Alias** **Purpose**" CR +" **array** **a** Sets watchpoint on array/object to observe if an entry is added or removed" CR +" **recursive** **r** Watches variable recursively and automatically adds watchpoints if some entry is added to an array/object" CR +" **delete** **d** Removes watchpoint" CR CR + +"Note when **recursive** watchpoints are removed, watchpoints on all the children are removed too" CR CR + +"**Examples**" CR CR +" $P watch" CR +" List currently active watchpoints" CR CR + +" $P watch $array" CR +" $P w $array" CR +" Set watchpoint on $array" CR CR + +" $P watch recursive $obj->" CR +" $P w r $obj->" CR +" Set recursive watchpoint on $obj->" CR CR + +" $P watch delete $obj->a" CR +" $P w d $obj->a" CR +" Remove watchpoint $obj->a" CR CR + +"Technical note: If using this feature with a debugger, you will get many segmentation faults, each time when a memory page containing a watched address is hit." CR +" You then you can continue, phpdbg will remove the write protection, so that the program can continue." CR +" If phpdbg could not handle that segfault, the same segfault is triggered again and this time phpdbg will abort." +}, +{NULL, NULL /* end of table marker */} +}; /* }}} */ diff --git a/phpdbg_help.h b/phpdbg_help.h index 319142cb5b..16a1e771e3 100644 --- a/phpdbg_help.h +++ b/phpdbg_help.h @@ -30,63 +30,19 @@ /** * Helper Forward Declarations */ -PHPDBG_HELP(exec); -PHPDBG_HELP(compile); -PHPDBG_HELP(step); -PHPDBG_HELP(next); -PHPDBG_HELP(run); -PHPDBG_HELP(eval); -PHPDBG_HELP(until); -PHPDBG_HELP(finish); -PHPDBG_HELP(leave); -PHPDBG_HELP(print); -PHPDBG_HELP(break); -PHPDBG_HELP(clean); -PHPDBG_HELP(clear); -PHPDBG_HELP(info); -PHPDBG_HELP(back); -PHPDBG_HELP(frame); -PHPDBG_HELP(quiet); -PHPDBG_HELP(list); -PHPDBG_HELP(set); -PHPDBG_HELP(register); -PHPDBG_HELP(options); -PHPDBG_HELP(source); -PHPDBG_HELP(shell); +PHPDBG_HELP(aliases); -/** - * Commands - */ -static const phpdbg_command_t phpdbg_help_commands[] = { - PHPDBG_COMMAND_D_EX(exec, "the execution context should be a valid path", 'e', help_exec, NULL, 0), - PHPDBG_COMMAND_D_EX(compile, "allow inspection of code before execution", 'c', help_compile, NULL, 0), - PHPDBG_COMMAND_D_EX(step, "step through execution to break at every opcode", 's', help_step, NULL, 0), - PHPDBG_COMMAND_D_EX(next, "continue executing while stepping or after breaking", 'n', help_next, NULL, 0), - PHPDBG_COMMAND_D_EX(run, "execute inside the phpdbg vm", 'r', help_run, NULL, 0), - PHPDBG_COMMAND_D_EX(eval, "access to eval() allows affecting the environment", 'E', help_eval, NULL, 0), - PHPDBG_COMMAND_D_EX(until, "continue until the current line is executed", 'u', help_until, NULL, 0), - PHPDBG_COMMAND_D_EX(finish, "continue until the current function has returned", 'F', help_finish, NULL, 0), - PHPDBG_COMMAND_D_EX(leave, "continue until the current function is returning", 'L', help_leave, NULL, 0), - PHPDBG_COMMAND_D_EX(print, "print context information or instructions", 'p', help_print, NULL, 0), - PHPDBG_COMMAND_D_EX(break, "breakpoints allow execution interruption", 'b', help_break, NULL, 0), - PHPDBG_COMMAND_D_EX(clean, "resetting the environment is useful while debugging", 'X', help_clean, NULL, 0), - PHPDBG_COMMAND_D_EX(clear, "reset breakpoints to execute without interruption", 'c', help_clear, NULL, 0), - PHPDBG_COMMAND_D_EX(info, "quick access to useful information on the console", 'i', help_info, NULL, 0), - PHPDBG_COMMAND_D_EX(back, "show debug backtrace information during execution", 't', help_back, NULL, 0), - PHPDBG_COMMAND_D_EX(frame, "switch to a frame in the current stack for inspection", 'f', help_frame, NULL, 0), - PHPDBG_COMMAND_D_EX(quiet, "be quiet during execution", 'Q', help_quiet, NULL, 0), - PHPDBG_COMMAND_D_EX(list, "list code gives you quick access to code", 'l', help_list, NULL, 0), - PHPDBG_COMMAND_D_EX(set, "configure how phpdbg looks and behaves", 'S', help_set, NULL, 0), - PHPDBG_COMMAND_D_EX(register, "register a function for use as a command", 'R', help_register,NULL, 0), - PHPDBG_COMMAND_D_EX(options, "show information about command line options", 'o', help_options, NULL, 0), - PHPDBG_COMMAND_D_EX(source, "load a phpdbginit file at the console", '.', help_source, NULL, 0), - PHPDBG_COMMAND_D_EX(shell, "execute system commands with direct shell access", '-', help_shell, NULL, 0), - PHPDBG_END_COMMAND -}; +extern const phpdbg_command_t phpdbg_help_commands[]; #define phpdbg_help_header() \ phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION); #define phpdbg_help_footer() \ phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES); +typedef struct _phpdbg_help_text_t { + char *key; + char *text; +} phpdbg_help_text_t; + +extern phpdbg_help_text_t phpdbg_help_text[]; #endif /* PHPDBG_HELP_H */ diff --git a/phpdbg_info.c b/phpdbg_info.c index 0f4233bf30..97f88bfa1e 100644 --- a/phpdbg_info.c +++ b/phpdbg_info.c @@ -23,9 +23,25 @@ #include "phpdbg_utils.h" #include "phpdbg_info.h" #include "phpdbg_bp.h" +#include "phpdbg_prompt.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); +#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14]) + +const phpdbg_command_t phpdbg_info_commands[] = { + PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0), + PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0), + PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0), + PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0), + PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0), + PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0), + PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0), + PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0), + PHPDBG_END_COMMAND +}; + PHPDBG_INFO(break) /* {{{ */ { phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC); diff --git a/phpdbg_info.h b/phpdbg_info.h index a6b4e3719f..c36e6bebd6 100644 --- a/phpdbg_info.h +++ b/phpdbg_info.h @@ -34,16 +34,6 @@ PHPDBG_INFO(vars); PHPDBG_INFO(literal); PHPDBG_INFO(memory); -static const phpdbg_command_t phpdbg_info_commands[] = { - PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0), - PHPDBG_COMMAND_D_EX(files, "show included files", 'F', info_files, NULL, 0), - PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0), - PHPDBG_COMMAND_D_EX(funcs, "show loaded classes", 'f', info_funcs, NULL, 0), - PHPDBG_COMMAND_D_EX(error, "show last error", 'e', info_error, NULL, 0), - PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0), - PHPDBG_COMMAND_D_EX(literal, "show active literal constants", 'l', info_literal, NULL, 0), - PHPDBG_COMMAND_D_EX(memory, "show memory manager stats", 'm', info_memory, NULL, 0), - PHPDBG_END_COMMAND -}; +extern const phpdbg_command_t phpdbg_info_commands[]; #endif /* PHPDBG_INFO_H */ diff --git a/phpdbg_lexer.c b/phpdbg_lexer.c new file mode 100644 index 0000000000..c289004b98 --- /dev/null +++ b/phpdbg_lexer.c @@ -0,0 +1,2271 @@ +#line 2 "sapi/phpdbg/phpdbg_lexer.c" + +#line 4 "sapi/phpdbg/phpdbg_lexer.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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 +#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 index 0000000000..1958cef9a2 --- /dev/null +++ b/phpdbg_lexer.h @@ -0,0 +1,348 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 6 "sapi/phpdbg/phpdbg_lexer.h" + +#line 8 "sapi/phpdbg/phpdbg_lexer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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 +#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 index 0000000000..ad5edd9f8f --- /dev/null +++ b/phpdbg_lexer.l @@ -0,0 +1,131 @@ +%{ + +/* + * phpdbg_lexer.l + */ + +#include "phpdbg.h" +#include "phpdbg_cmd.h" +#define YYSTYPE phpdbg_param_t + +#include "phpdbg_parser.h" + +%} + +%s RAW +%s NORMAL + +%option outfile="sapi/phpdbg/phpdbg_lexer.c" header-file="sapi/phpdbg/phpdbg_lexer.h" +%option warn nodefault + +%option reentrant noyywrap never-interactive nounistd +%option bison-bridge + +T_TRUE "true" +T_YES "yes" +T_ON "on" +T_ENABLED "enabled" +T_FALSE "false" +T_NO "no" +T_OFF "off" +T_DISABLED "disabled" +T_EVAL "ev" +T_SHELL "sh" +T_IF "if" +T_RUN "run" +T_RUN_SHORT "r" + +WS [ \r\n\t]+ +DIGITS [0-9\.]+ +ID [^ \r\n\t:#]+ +ADDR 0x[a-fA-F0-9]+ +OPCODE (ZEND_|zend_)([A-Za-z])+ +INPUT [^\n]+ +%% + +{ + {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; + } +} + +{ + {T_IF} { + BEGIN(RAW); + phpdbg_init_param(yylval, EMPTY_PARAM); + return T_IF; + } +} + +{ + {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; + } +} + +{INPUT} { + phpdbg_init_param(yylval, STR_PARAM); + yylval->str = strndup(yytext, yyleng); + yylval->len = yyleng; + BEGIN(INITIAL); + return T_INPUT; +} + +{WS} { /* ignore whitespace */ } +%% diff --git a/phpdbg_list.c b/phpdbg_list.c index eb1091550b..037c6c38b2 100644 --- a/phpdbg_list.c +++ b/phpdbg_list.c @@ -29,9 +29,22 @@ #include "phpdbg.h" #include "phpdbg_list.h" #include "phpdbg_utils.h" +#include "phpdbg_prompt.h" +#include "php_streams.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); +#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13]) + +const phpdbg_command_t phpdbg_list_commands[] = { + PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l"), + PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s"), + PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m"), + PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s"), + PHPDBG_END_COMMAND +}; + PHPDBG_LIST(lines) /* {{{ */ { if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) { @@ -41,12 +54,12 @@ PHPDBG_LIST(lines) /* {{{ */ switch (param->type) { case NUMERIC_PARAM: - case EMPTY_PARAM: phpdbg_list_file(phpdbg_current_file(TSRMLS_C), - param->type == EMPTY_PARAM ? 0 : (param->num < 0 ? 1 - param->num : param->num), - (param->type != EMPTY_PARAM && param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C), + (param->num < 0 ? 1 - param->num : param->num), + (param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C), 0 TSRMLS_CC); break; + case FILE_PARAM: phpdbg_list_file(param->file.name, param->file.line, 0, 0 TSRMLS_CC); break; @@ -59,41 +72,28 @@ PHPDBG_LIST(lines) /* {{{ */ PHPDBG_LIST(func) /* {{{ */ { - switch (param->type) { - case STR_PARAM: - phpdbg_list_function_byname( - param->str, param->len TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } + phpdbg_list_function_byname(param->str, param->len TSRMLS_CC); return SUCCESS; } /* }}} */ PHPDBG_LIST(method) /* {{{ */ { - switch (param->type) { - case METHOD_PARAM: { - zend_class_entry **ce; - - if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { - zend_function *function; - char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); + zend_class_entry **ce; - if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) { - phpdbg_list_function(function TSRMLS_CC); - } else { - phpdbg_error("Could not find %s::%s", param->method.class, param->method.name); - } + if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { + zend_function *function; + char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); - efree(lcname); - } else { - phpdbg_error("Could not find the class %s", param->method.class); - } - } break; + if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) { + phpdbg_list_function(function TSRMLS_CC); + } else { + phpdbg_error("Could not find %s::%s", param->method.class, param->method.name); + } - phpdbg_default_switch_case(); + efree(lcname); + } else { + phpdbg_error("Could not find the class %s", param->method.class); } return SUCCESS; @@ -101,30 +101,24 @@ PHPDBG_LIST(method) /* {{{ */ PHPDBG_LIST(class) /* {{{ */ { - switch (param->type) { - case STR_PARAM: { - zend_class_entry **ce; - - if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { - if ((*ce)->type == ZEND_USER_CLASS) { - if ((*ce)->info.user.filename) { - phpdbg_list_file( - (*ce)->info.user.filename, - (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1, - (*ce)->info.user.line_start, 0 TSRMLS_CC - ); - } else { - phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name); - } - } else { - phpdbg_error("The class requested (%s) is not user defined", (*ce)->name); - } + zend_class_entry **ce; + + if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { + if ((*ce)->type == ZEND_USER_CLASS) { + if ((*ce)->info.user.filename) { + phpdbg_list_file( + (*ce)->info.user.filename, + (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1, + (*ce)->info.user.line_start, 0 TSRMLS_CC + ); } else { - phpdbg_error("The requested class (%s) could not be found", param->str); + phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name); } - } break; - - phpdbg_default_switch_case(); + } else { + phpdbg_error("The class requested (%s) is not user defined", (*ce)->name); + } + } else { + phpdbg_error("The requested class (%s) could not be found", param->str); } return SUCCESS; @@ -132,97 +126,46 @@ PHPDBG_LIST(class) /* {{{ */ void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */ { - unsigned char *mem, *pos, *last_pos, *end_pos; struct stat st; -#ifndef _WIN32 - int fd; -#else - HANDLE fd, map; -#endif - int all_content = (count == 0); - int line = 0, displayed = 0; - + char *opened = NULL; + char buffer[8096] = {0,}; + long line = 0; + + php_stream *stream = NULL; + if (VCWD_STAT(filename, &st) == FAILURE) { phpdbg_error("Failed to stat file %s", filename); return; } - -#ifndef _WIN32 - if ((fd = VCWD_OPEN(filename, O_RDONLY)) == FAILURE) { + + stream = php_stream_open_wrapper(filename, "rb", USE_PATH, &opened); + + if (!stream) { phpdbg_error("Failed to open file %s to list", filename); return; } - - pos = last_pos = mem = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - end_pos = mem + st.st_size; -#else - - fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd == INVALID_HANDLE_VALUE) { - phpdbg_error("Failed to open file!"); - return; - } - - map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL); - if (map == NULL) { - phpdbg_error("Failed to map file!"); - CloseHandle(fd); - return; - } - - pos = last_pos = mem = (char*) MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); - if (mem == NULL) { - phpdbg_error("Failed to map file in memory"); - CloseHandle(map); - CloseHandle(fd); - return; - } - end_pos = mem + st.st_size; -#endif - while (1) { - if (pos == end_pos) { - break; - } - - pos = memchr(last_pos, '\n', end_pos - last_pos); - - if (!pos) { - /* No more line breaks */ - pos = end_pos; - } - + + while (php_stream_gets(stream, buffer, sizeof(buffer)) != NULL) { ++line; - + if (!offset || offset <= line) { /* Without offset, or offset reached */ if (!highlight) { - phpdbg_writeln("%05u: %.*s", line, (int)(pos - last_pos), last_pos); + phpdbg_write("%05ld: %s", line, buffer); } else { if (highlight != line) { - phpdbg_writeln(" %05u: %.*s", line, (int)(pos - last_pos), last_pos); + phpdbg_write(" %05ld: %s", line, buffer); } else { - phpdbg_writeln(">%05u: %.*s", line, (int)(pos - last_pos), last_pos); + phpdbg_write(">%05ld: %s", line, buffer); } } - ++displayed; } - - last_pos = pos + 1; - - if (!all_content && displayed == count) { - /* Reached max line to display */ + + if ((count + (offset-1)) == line) break; - } } - -#ifndef _WIN32 - munmap(mem, st.st_size); - close(fd); -#else - UnmapViewOfFile(mem); - CloseHandle(map); - CloseHandle(fd); -#endif + + php_stream_close(stream); } /* }}} */ void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */ diff --git a/phpdbg_list.h b/phpdbg_list.h index f9d1885eaf..14905f6567 100644 --- a/phpdbg_list.h +++ b/phpdbg_list.h @@ -36,12 +36,6 @@ void phpdbg_list_function_byname(const char *, size_t TSRMLS_DC); void phpdbg_list_function(const zend_function* TSRMLS_DC); void phpdbg_list_file(const char*, long, long, int TSRMLS_DC); -static const phpdbg_command_t phpdbg_list_commands[] = { - PHPDBG_COMMAND_D_EX(lines, "lists the specified lines", 'l', list_lines, NULL, 1), - PHPDBG_COMMAND_D_EX(class, "lists the specified class", 'c', list_class, NULL, 1), - PHPDBG_COMMAND_D_EX(method, "lists the specified method", 'm', list_method, NULL, 1), - PHPDBG_COMMAND_D_EX(func, "lists the specified function", 'f', list_func, NULL, 1), - PHPDBG_END_COMMAND -}; +extern const phpdbg_command_t phpdbg_list_commands[]; #endif /* PHPDBG_LIST_H */ diff --git a/phpdbg_opcode.c b/phpdbg_opcode.c index 50073eb22b..6b13625fc1 100644 --- a/phpdbg_opcode.c +++ b/phpdbg_opcode.c @@ -183,6 +183,7 @@ void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */ { +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO #define CASE(s) case s: return #s switch (opcode) { CASE(ZEND_NOP); @@ -360,4 +361,8 @@ const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */ default: return "UNKNOWN"; } +#else + const char *ret = zend_get_opcode_name(opcode); + return ret?ret:"UNKNOWN"; +#endif } /* }}} */ diff --git a/phpdbg_parser.y b/phpdbg_parser.y new file mode 100644 index 0000000000..4a84504e2e --- /dev/null +++ b/phpdbg_parser.y @@ -0,0 +1,168 @@ +%error-verbose +%{ + +/* + * phpdbg_parser.y + * (from php-src root) + * flex sapi/phpdbg/dev/phpdbg_lexer.l + * bison sapi/phpdbg/dev/phpdbg_parser.y + */ + +#include "phpdbg.h" +#include "phpdbg_cmd.h" +#include "phpdbg_utils.h" +#include "phpdbg_cmd.h" +#include "phpdbg_prompt.h" + +#define YYSTYPE phpdbg_param_t + +#include "phpdbg_parser.h" +#include "phpdbg_lexer.h" + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +int yyerror(phpdbg_param_t *stack, yyscan_t scanner, const char *msg) { + TSRMLS_FETCH(); + phpdbg_error("Parse Error: %s", msg); + { + const phpdbg_param_t *top = stack; + + while (top) { + phpdbg_param_debug( + top, "--> "); + top = top->next; + } + } + return 0; +} +%} + +%code requires { +#include "phpdbg.h" +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif +} +%output "sapi/phpdbg/phpdbg_parser.c" +%defines "sapi/phpdbg/phpdbg_parser.h" + +%define api.pure +%lex-param { yyscan_t scanner } +%parse-param { phpdbg_param_t *stack } +%parse-param { yyscan_t scanner } + +%token T_EVAL "eval" +%token T_RUN "run" +%token T_SHELL "shell" +%token T_IF "if (condition)" +%token T_TRUTHY "truthy (true, on, yes or enabled)" +%token T_FALSY "falsy (false, off, no or disabled)" +%token T_STRING "string (some input, perhaps)" +%token T_COLON ": (colon)" +%token T_DCOLON ":: (double colon)" +%token T_POUND "# (pound sign)" +%token T_PROTO "protocol (file://)" +%token T_DIGITS "digits (numbers)" +%token T_LITERAL "literal (string)" +%token T_ADDR "address" +%token T_OPCODE "opcode" +%token T_ID "identifier (command or function name)" +%token T_INPUT "input (input string or data)" +%token T_UNEXPECTED "input" +%% + +input + : parameters + | /* nothing */ + ; + +parameters + : parameter { phpdbg_stack_push(stack, &$1); } + | parameters parameter { phpdbg_stack_push(stack, &$2); } + ; + +parameter + : T_ID T_COLON T_DIGITS { + $$.type = FILE_PARAM; + $$.file.name = $2.str; + $$.file.line = $3.num; + } + | T_ID T_COLON T_POUND T_DIGITS { + $$.type = NUMERIC_FILE_PARAM; + $$.file.name = $1.str; + $$.file.line = $4.num; + } + | T_PROTO T_ID T_COLON T_DIGITS { + $$.type = FILE_PARAM; + $$.file.name = malloc($1.len + + $2.len + 1); + if ($$.file.name) { + memcpy(&$$.file.name[0], $1.str, $1.len); + memcpy(&$$.file.name[$1.len], $2.str, $2.len); + $$.file.name[$1.len + $2.len] = '\0'; + } + $$.file.line = $4.num; + } + | T_PROTO T_ID T_COLON T_POUND T_DIGITS { + $$.type = NUMERIC_FILE_PARAM; + $$.file.name = malloc($1.len + + $2.len + 1); + if ($$.file.name) { + memcpy(&$$.file.name[0], $1.str, $1.len); + memcpy(&$$.file.name[$1.len], $2.str, $2.len); + $$.file.name[$1.len + $2.len] = '\0'; + } + $$.file.line = $5.num; + } + | T_ID T_DCOLON T_ID { + $$.type = METHOD_PARAM; + $$.method.class = $1.str; + $$.method.name = $3.str; + } + | T_ID T_DCOLON T_ID T_POUND T_DIGITS { + $$.type = NUMERIC_METHOD_PARAM; + $$.method.class = $1.str; + $$.method.name = $3.str; + $$.num = $5.num; + } + | T_ID T_POUND T_DIGITS { + $$.type = NUMERIC_FUNCTION_PARAM; + $$.str = $1.str; + $$.len = $1.len; + $$.num = $3.num; + } + | T_IF T_INPUT { + $$.type = COND_PARAM; + $$.str = $2.str; + $$.len = $2.len; + } + | T_EVAL T_INPUT { + $$.type = EVAL_PARAM; + $$.str = $2.str; + $$.len = $2.len; + } + | T_SHELL T_INPUT { + $$.type = SHELL_PARAM; + $$.str = $2.str; + $$.len = $2.len; + } + | T_RUN { + $$.type = RUN_PARAM; + $$.len = 0; + } + | T_RUN T_INPUT { + $$.type = RUN_PARAM; + $$.str = $2.str; + $$.len = $2.len; + } + | T_OPCODE { $$ = $1; } + | T_ADDR { $$ = $1; } + | T_LITERAL { $$ = $1; } + | T_TRUTHY { $$ = $1; } + | T_FALSY { $$ = $1; } + | T_DIGITS { $$ = $1; } + | T_ID { $$ = $1; } + ; + +%% diff --git a/phpdbg_print.c b/phpdbg_print.c index c4925fe5fd..76321a5042 100644 --- a/phpdbg_print.c +++ b/phpdbg_print.c @@ -26,6 +26,19 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); +#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9]) + +const phpdbg_command_t phpdbg_print_commands[] = { + PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0), + PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0), + PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s"), + PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m"), + PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s"), + PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0), + PHPDBG_END_COMMAND +}; + PHPDBG_PRINT(opline) /* {{{ */ { if (EG(in_execution) && EG(current_execute_data)) { @@ -77,7 +90,7 @@ static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC) phpdbg_error("\tFailed to decode opline %16p", opline); } opline++; - } while (++opcode < end); + } while (opcode++ < end); zend_hash_destroy(&vars); } } break; @@ -141,36 +154,30 @@ PHPDBG_PRINT(class) /* {{{ */ { zend_class_entry **ce; - switch (param->type) { - case STR_PARAM: { - if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { - phpdbg_notice("%s %s: %s", - ((*ce)->type == ZEND_USER_CLASS) ? - "User" : "Internal", - ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ? - "Interface" : - ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ? - "Abstract Class" : - "Class", - (*ce)->name); - - phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table)); - if (zend_hash_num_elements(&(*ce)->function_table)) { - HashPosition position; - zend_function *method; - - for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position); - zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS; - zend_hash_move_forward_ex(&(*ce)->function_table, &position)) { - phpdbg_print_function_helper(method TSRMLS_CC); - } - } - } else { - phpdbg_error("The class %s could not be found", param->str); + if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) { + phpdbg_notice("%s %s: %s", + ((*ce)->type == ZEND_USER_CLASS) ? + "User" : "Internal", + ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ? + "Interface" : + ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ? + "Abstract Class" : + "Class", + (*ce)->name); + + phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table)); + if (zend_hash_num_elements(&(*ce)->function_table)) { + HashPosition position; + zend_function *method; + + for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position); + zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS; + zend_hash_move_forward_ex(&(*ce)->function_table, &position)) { + phpdbg_print_function_helper(method TSRMLS_CC); } - } break; - - phpdbg_default_switch_case(); + } + } else { + phpdbg_error("The class %s could not be found", param->str); } return SUCCESS; @@ -178,31 +185,25 @@ PHPDBG_PRINT(class) /* {{{ */ PHPDBG_PRINT(method) /* {{{ */ { - switch (param->type) { - case METHOD_PARAM: { - zend_class_entry **ce; - - if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { - zend_function *fbc; - char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); + zend_class_entry **ce; - if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) { - phpdbg_notice("%s Method %s", - (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal", - fbc->common.function_name); + if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) { + zend_function *fbc; + char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); - phpdbg_print_function_helper(fbc TSRMLS_CC); - } else { - phpdbg_error("The method %s could not be found", param->method.name); - } + if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) { + phpdbg_notice("%s Method %s", + (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal", + fbc->common.function_name); - efree(lcname); - } else { - phpdbg_error("The class %s could not be found", param->method.class); - } - } break; + phpdbg_print_function_helper(fbc TSRMLS_CC); + } else { + phpdbg_error("The method %s could not be found", param->method.name); + } - phpdbg_default_switch_case(); + efree(lcname); + } else { + phpdbg_error("The class %s could not be found", param->method.class); } return SUCCESS; @@ -210,49 +211,43 @@ PHPDBG_PRINT(method) /* {{{ */ PHPDBG_PRINT(func) /* {{{ */ { - switch (param->type) { - case STR_PARAM: { - HashTable *func_table = EG(function_table); - zend_function* fbc; - const char *func_name = param->str; - size_t func_name_len = param->len; - char *lcname; - /* search active scope if begins with period */ - if (func_name[0] == '.') { - if (EG(scope)) { - func_name++; - func_name_len--; - - func_table = &EG(scope)->function_table; - } else { - phpdbg_error("No active class"); - return SUCCESS; - } - } else if (!EG(function_table)) { - phpdbg_error("No function table loaded"); - return SUCCESS; - } else { - func_table = EG(function_table); - } - - lcname = zend_str_tolower_dup(func_name, func_name_len); - - if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) { - phpdbg_notice("%s %s %s", - (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal", - (fbc->common.scope) ? "Method" : "Function", - fbc->common.function_name); + HashTable *func_table = EG(function_table); + zend_function* fbc; + const char *func_name = param->str; + size_t func_name_len = param->len; + char *lcname; + /* search active scope if begins with period */ + if (func_name[0] == '.') { + if (EG(scope)) { + func_name++; + func_name_len--; + + func_table = &EG(scope)->function_table; + } else { + phpdbg_error("No active class"); + return SUCCESS; + } + } else if (!EG(function_table)) { + phpdbg_error("No function table loaded"); + return SUCCESS; + } else { + func_table = EG(function_table); + } - phpdbg_print_function_helper(fbc TSRMLS_CC); - } else { - phpdbg_error("The function %s could not be found", func_name); - } + lcname = zend_str_tolower_dup(func_name, func_name_len); - efree(lcname); - } break; + if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) { + phpdbg_notice("%s %s %s", + (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal", + (fbc->common.scope) ? "Method" : "Function", + fbc->common.function_name); - phpdbg_default_switch_case(); + phpdbg_print_function_helper(fbc TSRMLS_CC); + } else { + phpdbg_error("The function %s could not be found", func_name); } + efree(lcname); + return SUCCESS; } /* }}} */ diff --git a/phpdbg_print.h b/phpdbg_print.h index 80010d5e7c..ed85e965c1 100644 --- a/phpdbg_print.h +++ b/phpdbg_print.h @@ -35,17 +35,6 @@ PHPDBG_PRINT(method); PHPDBG_PRINT(func); PHPDBG_PRINT(stack); -/** - * Commands - */ -static const phpdbg_command_t phpdbg_print_commands[] = { - PHPDBG_COMMAND_D_EX(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0), - PHPDBG_COMMAND_D_EX(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0), - PHPDBG_COMMAND_D_EX(class, "print out the instructions in the specified class", 'c', print_class, NULL, 1), - PHPDBG_COMMAND_D_EX(method, "print out the instructions in the specified method", 'm', print_method, NULL, 1), - PHPDBG_COMMAND_D_EX(func, "print out the instructions in the specified function", 'f', print_func, NULL, 1), - PHPDBG_COMMAND_D_EX(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0), - PHPDBG_END_COMMAND -}; +extern const phpdbg_command_t phpdbg_print_commands[]; #endif /* PHPDBG_PRINT_H */ diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index f586bb5a84..7a28a75129 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -35,99 +35,154 @@ #include "phpdbg_cmd.h" #include "phpdbg_set.h" #include "phpdbg_frame.h" +#include "phpdbg_lexer.h" +#include "phpdbg_parser.h" + +int yyparse(phpdbg_param_t *stack, yyscan_t scanner); /* {{{ command declarations */ const phpdbg_command_t phpdbg_prompt_commands[] = { - PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1), - PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0), - PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1), - PHPDBG_COMMAND_D(next, "continue execution", 'n', NULL, 0), - PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0), - PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1), + PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s"), + PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0), + PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0), + PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s"), + PHPDBG_COMMAND_D(ev, "evaluate some code", 0, NULL, "i"), PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0), PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0), PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0), - PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2), - PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1), - PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0), - PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1), - PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, 2), - PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, 1), + PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0), + PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c"), + PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n"), + PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n"), + PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*"), + PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s"), PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0), PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0), - PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, 2), - PHPDBG_COMMAND_D(quiet, "silence some output", 'Q', NULL, 1), - PHPDBG_COMMAND_D(aliases, "show alias list", 'a', NULL, 0), - PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, 1), - PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, 1), - PHPDBG_COMMAND_D(source, "execute a phpdbginit", '.', NULL, 1), - PHPDBG_COMMAND_D(shell, "shell a command", '-', NULL, 1), + PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s"), + PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s"), + PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s"), + PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s"), + PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s"), + PHPDBG_COMMAND_D(sh, "shell a command", 0, NULL, "i"), PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0), + PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss"), PHPDBG_END_COMMAND }; /* }}} */ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +static inline int phpdbg_call_register(phpdbg_param_t *stack TSRMLS_DC) /* {{{ */ { - phpdbg_input_t *function = input->argv[0]; - - if (zend_hash_exists( - &PHPDBG_G(registered), function->string, function->length+1)) { - - zval fname, *fretval; - zend_fcall_info fci; - - ZVAL_STRINGL(&fname, function->string, function->length, 1); - - memset(&fci, 0, sizeof(zend_fcall_info)); - - fci.size = sizeof(zend_fcall_info); - fci.function_table = &PHPDBG_G(registered); - fci.function_name = &fname; - fci.symbol_table = EG(active_symbol_table); - fci.object_ptr = NULL; - fci.retval_ptr_ptr = &fretval; - fci.no_separation = 1; - - if (input->argc > 1) { - int param; - zval params; - - array_init(¶ms); + phpdbg_param_t *name = NULL; - for (param = 0; param < (input->argc-1); param++) { - add_next_index_stringl( - ¶ms, - 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(¶ms); + + while (next) { + char *buffered = NULL; + + switch (next->type) { + case OP_PARAM: + case COND_PARAM: + case STR_PARAM: + add_next_index_stringl( + ¶ms, + next->str, + next->len, 1); + break; + + case NUMERIC_PARAM: + add_next_index_long(¶ms, next->num); + break; + + case METHOD_PARAM: + spprintf(&buffered, 0, "%s::%s", + next->method.class, next->method.name); + add_next_index_string(¶ms, 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(¶ms, buffered, 0); + break; + + case NUMERIC_FUNCTION_PARAM: + spprintf(&buffered, 0, "%s#%ld", + next->str, next->num); + add_next_index_string(¶ms, buffered, 0); + break; + + case FILE_PARAM: + spprintf(&buffered, 0, "%s:%ld", + next->file.name, next->file.line); + add_next_index_string(¶ms, buffered, 0); + break; + + case NUMERIC_FILE_PARAM: + spprintf(&buffered, 0, "%s:#%ld", + next->file.name, next->file.line); + add_next_index_string(¶ms, 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, ¶ms TSRMLS_CC); + } else { + fci.params = NULL; + fci.param_count = 0; } - zend_fcall_info_args(&fci, ¶ms TSRMLS_CC); - } else { - fci.params = NULL; - fci.param_count = 0; - } - - phpdbg_debug( - "created %d params from %d arguments", - fci.param_count, input->argc); + phpdbg_debug( + "created %d params from arguments", + fci.param_count); - zend_call_function(&fci, NULL TSRMLS_CC); + zend_call_function(&fci, NULL TSRMLS_CC); - if (fretval) { - zend_print_zval_r( - fretval, 0 TSRMLS_CC); - phpdbg_writeln(EMPTY); - } + if (fretval) { + zend_print_zval_r( + fretval, 0 TSRMLS_CC); + phpdbg_writeln(EMPTY); + } - zval_dtor(&fname); + zval_dtor(&fname); - return SUCCESS; + return SUCCESS; + } } return FAILURE; @@ -136,7 +191,7 @@ static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ * void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC) /* {{{ */ { struct stat sb; - + if (init_file && VCWD_STAT(init_file, &sb) != -1) { FILE *fp = fopen(init_file, "r"); if (fp) { @@ -190,19 +245,47 @@ void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_ } { - phpdbg_input_t *input = phpdbg_read_input(cmd TSRMLS_CC); - switch (phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) { - case FAILURE: - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) { - phpdbg_error("Unrecognized command in %s:%d: %s!", init_file, line, input->string); - } - } + char *why = NULL; + char *input = phpdbg_read_input(cmd TSRMLS_CC); + phpdbg_param_t stack; + yyscan_t scanner; + YY_BUFFER_STATE state; + + phpdbg_init_param(&stack, STACK_PARAM); + + if (yylex_init(&scanner)) { + phpdbg_error( + "could not initialize scanner"); break; } + + state = yy_scan_string(input, scanner); + + if (yyparse(&stack, scanner) <= 0) { + switch (phpdbg_stack_execute(&stack, &why TSRMLS_CC)) { + case FAILURE: +// if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { + phpdbg_error( + "Unrecognized command in %s:%d: %s, %s!", + init_file, line, input, why); + } +// } + break; + } + } + + if (why) { + free(why); + why = NULL; + } + + yy_delete_buffer(state, scanner); + yylex_destroy(scanner); + + phpdbg_stack_free(&stack); phpdbg_destroy_input(&input TSRMLS_CC); } - } next_line: line++; @@ -264,47 +347,47 @@ void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TS PHPDBG_COMMAND(exec) /* {{{ */ { - switch (param->type) { - case STR_PARAM: { - struct stat sb; - - if (VCWD_STAT(param->str, &sb) != FAILURE) { - if (sb.st_mode & (S_IFREG|S_IFLNK)) { - char *res = phpdbg_resolve_path(param->str TSRMLS_CC); - size_t res_len = strlen(res); - - if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { - - if (PHPDBG_G(exec)) { - phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec)); - efree(PHPDBG_G(exec)); - PHPDBG_G(exec) = NULL; - PHPDBG_G(exec_len) = 0L; - } + struct stat sb; - if (PHPDBG_G(ops)) { - phpdbg_notice("Destroying compiled opcodes"); - phpdbg_clean(0 TSRMLS_CC); - } + if (VCWD_STAT(param->str, &sb) != FAILURE) { + if (sb.st_mode & (S_IFREG|S_IFLNK)) { + char *res = phpdbg_resolve_path(param->str TSRMLS_CC); + size_t res_len = strlen(res); - PHPDBG_G(exec) = res; - PHPDBG_G(exec_len) = res_len; + if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { - phpdbg_notice("Set execution context: %s", PHPDBG_G(exec)); - } else { - phpdbg_notice("Execution context not changed"); - } - } else { - phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str); + if (PHPDBG_G(exec)) { + phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec)); + efree(PHPDBG_G(exec)); + PHPDBG_G(exec) = NULL; + PHPDBG_G(exec_len) = 0L; + } + + if (PHPDBG_G(ops)) { + phpdbg_notice("Destroying compiled opcodes"); + phpdbg_clean(0 TSRMLS_CC); + } + + PHPDBG_G(exec) = res; + PHPDBG_G(exec_len) = res_len; + + *SG(request_info).argv = PHPDBG_G(exec); + php_hash_environment(TSRMLS_C); + + phpdbg_notice("Set execution context: %s", PHPDBG_G(exec)); + + if (phpdbg_compile(TSRMLS_C) == FAILURE) { + phpdbg_error("Failed to compile %s", PHPDBG_G(exec)); } } else { - phpdbg_error("Cannot stat %s, ensure the file exists", param->str); + phpdbg_notice("Execution context not changed"); } - } break; - - phpdbg_default_switch_case(); + } else { + phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str); + } + } else { + phpdbg_error("Cannot stat %s, ensure the file exists", param->str); } - return SUCCESS; } /* }}} */ @@ -312,6 +395,11 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */ { zend_file_handle fh; + if (!PHPDBG_G(exec)) { + phpdbg_error("No execution context"); + return SUCCESS; + } + if (EG(in_execution)) { phpdbg_error("Cannot compile while in execution"); return FAILURE; @@ -334,47 +422,16 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */ return FAILURE; } /* }}} */ -PHPDBG_COMMAND(compile) /* {{{ */ -{ - if (!PHPDBG_G(exec)) { - phpdbg_error("No execution context"); - return SUCCESS; - } - - if (!EG(in_execution)) { - if (PHPDBG_G(ops)) { - phpdbg_error("Destroying previously compiled opcodes"); - phpdbg_clean(0 TSRMLS_CC); - } - } - - phpdbg_compile(TSRMLS_C); - - return SUCCESS; -} /* }}} */ - PHPDBG_COMMAND(step) /* {{{ */ { - switch (param->type) { - case EMPTY_PARAM: - case NUMERIC_PARAM: { - if (param->type == NUMERIC_PARAM && param->num) { - PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; - } else { - PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING; - } - - phpdbg_notice("Stepping %s", - (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); - } break; - - phpdbg_default_switch_case(); + if (EG(in_execution)) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; } - return SUCCESS; + return PHPDBG_NEXT; } /* }}} */ -PHPDBG_COMMAND(next) /* {{{ */ +PHPDBG_COMMAND(continue) /* {{{ */ { return PHPDBG_NEXT; } /* }}} */ @@ -475,17 +532,9 @@ PHPDBG_COMMAND(leave) /* {{{ */ PHPDBG_COMMAND(frame) /* {{{ */ { - switch (param->type) { - case NUMERIC_PARAM: - phpdbg_switch_frame(param->num TSRMLS_CC); - break; - - case EMPTY_PARAM: - phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num); - break; - - phpdbg_default_switch_case(); - } + if (!param) { + phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num); + } else phpdbg_switch_frame(param->num TSRMLS_CC); return SUCCESS; } /* }}} */ @@ -580,6 +629,31 @@ PHPDBG_COMMAND(run) /* {{{ */ /* reset hit counters */ phpdbg_reset_breakpoints(TSRMLS_C); + if (param && param->type != EMPTY_PARAM && param->len != 0) { + char **argv = emalloc(5 * sizeof(char *)); + int argc = 0; + int i; + char *argv_str = strtok(param->str, " "); + + while (argv_str) { + if (argc >= 4 && argc == (argc & -argc)) { + argv = erealloc(argv, (argc * 2 + 1) * sizeof(char *)); + } + argv[++argc] = argv_str; + argv_str = strtok(0, " "); + argv[argc] = estrdup(argv[argc]); + } + argv[0] = SG(request_info).argv[0]; + for (i = SG(request_info).argc; --i;) { + efree(SG(request_info).argv[i]); + } + efree(SG(request_info).argv); + SG(request_info).argv = erealloc(argv, ++argc * sizeof(char *)); + SG(request_info).argc = argc; + + php_hash_environment(TSRMLS_C); + } + zend_try { php_output_activate(TSRMLS_C); PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE; @@ -615,42 +689,36 @@ out: return SUCCESS; } /* }}} */ -PHPDBG_COMMAND(eval) /* {{{ */ +PHPDBG_COMMAND(ev) /* {{{ */ { - switch (param->type) { - case STR_PARAM: { - zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING); - zval retval; - - if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { - PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING; - } - - /* disable stepping while eval() in progress */ - PHPDBG_G(flags) |= PHPDBG_IN_EVAL; - zend_try { - if (zend_eval_stringl(param->str, param->len, - &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) { - zend_print_zval_r( - &retval, 0 TSRMLS_CC); - phpdbg_writeln(EMPTY); - zval_dtor(&retval); - } - } zend_end_try(); - PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL; + zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING); + zval retval; - /* switch stepping back on */ - if (stepping && - !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { - PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; - } + if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING; + } - CG(unclean_shutdown) = 0; - } break; + /* disable stepping while eval() in progress */ + PHPDBG_G(flags) |= PHPDBG_IN_EVAL; + zend_try { + if (zend_eval_stringl(param->str, param->len, + &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) { + zend_print_zval_r( + &retval, 0 TSRMLS_CC); + phpdbg_writeln(EMPTY); + zval_dtor(&retval); + } + } zend_end_try(); + PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL; - phpdbg_default_switch_case(); + /* switch stepping back on */ + if (stepping && + !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; } + CG(unclean_shutdown) = 0; + return SUCCESS; } /* }}} */ @@ -661,14 +729,10 @@ PHPDBG_COMMAND(back) /* {{{ */ return SUCCESS; } - switch (param->type) { - case EMPTY_PARAM: - case NUMERIC_PARAM: - phpdbg_dump_backtrace( - (param->type == NUMERIC_PARAM) ? param->num : 0 TSRMLS_CC); - break; - - phpdbg_default_switch_case(); + if (!param) { + phpdbg_dump_backtrace(0 TSRMLS_CC); + } else { + phpdbg_dump_backtrace(param->num TSRMLS_CC); } return SUCCESS; @@ -676,47 +740,41 @@ PHPDBG_COMMAND(back) /* {{{ */ PHPDBG_COMMAND(print) /* {{{ */ { - switch (param->type) { - case EMPTY_PARAM: { - phpdbg_writeln(SEPARATE); - phpdbg_notice("Execution Context Information"); + phpdbg_writeln(SEPARATE); + phpdbg_notice("Execution Context Information"); #ifdef HAVE_LIBREADLINE - phpdbg_writeln("Readline\tyes"); + phpdbg_writeln("Readline\tyes"); #else - phpdbg_writeln("Readline\tno"); + phpdbg_writeln("Readline\tno"); #endif - phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none"); - phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no"); - phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); - phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off"); - phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off"); + phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none"); + phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no"); + phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); + phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off"); + phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off"); - if (PHPDBG_G(ops)) { - phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last); - - if (PHPDBG_G(ops)->last_var) { - phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1); - } else { - phpdbg_writeln("Variables\tNone"); - } - } + if (PHPDBG_G(ops)) { + phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last); - phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no"); - if (EG(in_execution)) { - phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret)); - } + if (PHPDBG_G(ops)->last_var) { + phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1); + } else { + phpdbg_writeln("Variables\tNone"); + } + } - phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table))); - phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table))); - phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants))); - phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files))); + phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no"); + if (EG(in_execution)) { + phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret)); + } - phpdbg_writeln(SEPARATE); - } break; + phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files))); - phpdbg_default_switch_case(); - } + phpdbg_writeln(SEPARATE); return SUCCESS; } /* }}} */ @@ -732,19 +790,18 @@ PHPDBG_COMMAND(info) /* {{{ */ PHPDBG_COMMAND(set) /* {{{ */ { phpdbg_error( - "No information command selected!"); + "No set command selected!"); return SUCCESS; } /* }}} */ PHPDBG_COMMAND(break) /* {{{ */ { - switch (param->type) { - case EMPTY_PARAM: - phpdbg_set_breakpoint_file( + if (!param) { + phpdbg_set_breakpoint_file( zend_get_executed_filename(TSRMLS_C), zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC); - break; + } else switch (param->type) { case ADDR_PARAM: phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC); break; @@ -767,9 +824,18 @@ PHPDBG_COMMAND(break) /* {{{ */ case FILE_PARAM: phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC); break; + case NUMERIC_FILE_PARAM: + phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC); + break; + case COND_PARAM: + phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC); + break; case STR_PARAM: phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC); break; + case OP_PARAM: + phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC); + break; phpdbg_default_switch_case(); } @@ -777,83 +843,72 @@ PHPDBG_COMMAND(break) /* {{{ */ return SUCCESS; } /* }}} */ -PHPDBG_COMMAND(shell) /* {{{ */ +PHPDBG_COMMAND(sh) /* {{{ */ { - /* don't allow this to loop, ever ... */ - switch (param->type) { - case STR_PARAM: { - FILE *fd = NULL; - if ((fd=VCWD_POPEN((char*)param->str, "w"))) { - /* do something perhaps ?? do we want input ?? */ - fclose(fd); - } else { - phpdbg_error( - "Failed to execute %s", param->str); - } - } break; - - phpdbg_default_switch_case(); + FILE *fd = NULL; + if ((fd=VCWD_POPEN((char*)param->str, "w"))) { + /* do something perhaps ?? do we want input ?? */ + fclose(fd); + } else { + phpdbg_error( + "Failed to execute %s", param->str); } + return SUCCESS; } /* }}} */ PHPDBG_COMMAND(source) /* {{{ */ { - switch (param->type) { - case STR_PARAM: { - if (input->argc > 2) { - if (phpdbg_argv_is(1, "export")) { - FILE *h = VCWD_FOPEN(input->argv[2]->string, "w+"); - if (h) { - phpdbg_export_breakpoints(h TSRMLS_CC); - fclose(h); - } else phpdbg_error("Failed to open %s", input->argv[1]->string); - } else { - phpdbg_error( - "Incorrect usage of source command, see help"); - } - } else { - struct stat sb; - if (VCWD_STAT(param->str, &sb) != -1) { - phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC); - } else phpdbg_error("Cannot stat %s", param->str); - } - } break; + struct stat sb; + + if (VCWD_STAT(param->str, &sb) != -1) { + phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC); + } else { + phpdbg_error( + "Failed to stat %s, file does not exist", param->str); + } + + return SUCCESS; +} /* }}} */ - phpdbg_default_switch_case(); +PHPDBG_COMMAND(export) /* {{{ */ +{ + FILE *handle = VCWD_FOPEN(param->str, "w+"); + + if (handle) { + phpdbg_export_breakpoints(handle TSRMLS_CC); + fclose(handle); + } else { + phpdbg_error( + "Failed to open or create %s, check path and permissions", param->str); } + return SUCCESS; } /* }}} */ PHPDBG_COMMAND(register) /* {{{ */ { - switch (param->type) { - case STR_PARAM: { - zend_function *function; - char *lcname = zend_str_tolower_dup(param->str, param->len); - size_t lcname_len = strlen(lcname); - - if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) { - if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) { - zend_hash_update( - &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL); - function_add_ref(function); - - phpdbg_notice( - "Registered %s", lcname); - } else { - phpdbg_error("The requested function (%s) could not be found", param->str); - } - } else { - phpdbg_error( - "The requested name (%s) is already in use", lcname); - } - - efree(lcname); - } break; - - phpdbg_default_switch_case(); + zend_function *function; + char *lcname = zend_str_tolower_dup(param->str, param->len); + size_t lcname_len = strlen(lcname); + + if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) { + if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) { + zend_hash_update( + &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL); + function_add_ref(function); + + phpdbg_notice( + "Registered %s", lcname); + } else { + phpdbg_error("The requested function (%s) could not be found", param->str); + } + } else { + phpdbg_error( + "The requested name (%s) is already in use", lcname); } + + efree(lcname); return SUCCESS; } /* }}} */ @@ -861,14 +916,11 @@ PHPDBG_COMMAND(quit) /* {{{ */ { /* don't allow this to loop, ever ... */ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - - phpdbg_destroy_input((phpdbg_input_t**)&input TSRMLS_CC); - PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; zend_bailout(); } - return SUCCESS; + return PHPDBG_NEXT; } /* }}} */ PHPDBG_COMMAND(clean) /* {{{ */ @@ -908,100 +960,12 @@ PHPDBG_COMMAND(clear) /* {{{ */ return SUCCESS; } /* }}} */ -PHPDBG_COMMAND(aliases) /* {{{ */ -{ - const phpdbg_command_t *prompt_command = phpdbg_prompt_commands; - - phpdbg_help_header(); - phpdbg_writeln("Below are the aliased, short versions of all supported commands"); - while (prompt_command && prompt_command->name) { - if (prompt_command->alias) { - if (prompt_command->subs) { - const phpdbg_command_t *sub_command = prompt_command->subs; - phpdbg_writeln(EMPTY); - phpdbg_writeln(" %c -> %9s", prompt_command->alias, prompt_command->name); - while (sub_command && sub_command->name) { - if (sub_command->alias) { - phpdbg_writeln(" |-------- %c -> %15s\t%s", sub_command->alias, - sub_command->name, sub_command->tip); - } - ++sub_command; - } - phpdbg_writeln(EMPTY); - } else { - phpdbg_writeln(" %c -> %9s\t\t\t%s", prompt_command->alias, - prompt_command->name, prompt_command->tip); - } - } - - ++prompt_command; - } - phpdbg_help_footer(); - - return SUCCESS; -} /* }}} */ - -PHPDBG_COMMAND(help) /* {{{ */ -{ - switch (param->type) { - case EMPTY_PARAM: { - const phpdbg_command_t *prompt_command = phpdbg_prompt_commands; - const phpdbg_command_t *help_command = phpdbg_help_commands; - - phpdbg_help_header(); - phpdbg_writeln("To get help regarding a specific command type \"help command\""); - - phpdbg_notice("Commands"); - - while (prompt_command && prompt_command->name) { - phpdbg_writeln( - " %10s\t%s", prompt_command->name, prompt_command->tip); - ++prompt_command; - } - - phpdbg_notice("Help Commands"); - - while (help_command && help_command->name) { - phpdbg_writeln(" %10s\t%s", help_command->name, help_command->tip); - ++help_command; - } - - phpdbg_help_footer(); - } break; - - default: { - phpdbg_error( - "No help can be found for the subject \"%s\"", param->str); - } - } - - return SUCCESS; -} /* }}} */ - -PHPDBG_COMMAND(quiet) /* {{{ */ -{ - switch (param->type) { - case NUMERIC_PARAM: { - if (param->num) { - PHPDBG_G(flags) |= PHPDBG_IS_QUIET; - } else { - PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET; - } - phpdbg_notice("Quietness %s", - (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "enabled" : "disabled"); - } break; - - phpdbg_default_switch_case(); - } - - return SUCCESS; -} /* }}} */ - PHPDBG_COMMAND(list) /* {{{ */ { - switch (param->type) { + if (!param) { + return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); + } else switch (param->type) { case NUMERIC_PARAM: - case EMPTY_PARAM: return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); case FILE_PARAM: @@ -1020,54 +984,101 @@ PHPDBG_COMMAND(list) /* {{{ */ return SUCCESS; } /* }}} */ +PHPDBG_COMMAND(watch) /* {{{ */ +{ + if (!param || param->type == EMPTY_PARAM) { + phpdbg_list_watchpoints(TSRMLS_C); + } else switch (param->type) { + case STR_PARAM: + if (phpdbg_create_var_watchpoint(param->str, param->len TSRMLS_CC) != FAILURE) { + phpdbg_notice("Set watchpoint on %.*s", (int)param->len, param->str); + } + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + int phpdbg_interactive(TSRMLS_D) /* {{{ */ { int ret = SUCCESS; - phpdbg_input_t *input; + char *why = NULL; + char *input = NULL; + phpdbg_param_t stack; PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE; input = phpdbg_read_input(NULL TSRMLS_CC); - if (input && input->length > 0L) { + if (input) { do { - switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) { - case FAILURE: - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) { - phpdbg_error("Failed to execute %s!", input->string); + yyscan_t scanner; + YY_BUFFER_STATE state; + + phpdbg_init_param(&stack, STACK_PARAM); + + if (yylex_init(&scanner)) { + phpdbg_error( + "could not initialize scanner"); + return FAILURE; + } + + state = yy_scan_string(input, scanner); + + if (yyparse(&stack, scanner) <= 0) { + switch (ret = phpdbg_stack_execute(&stack, &why TSRMLS_CC)) { + case FAILURE: + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { + if (why) { + phpdbg_error("%s", why); + } + } } - } - break; - case PHPDBG_LEAVE: - case PHPDBG_FINISH: - case PHPDBG_UNTIL: - case PHPDBG_NEXT: { - if (!EG(in_execution)) { - phpdbg_error("Not running"); + if (why) { + free(why); + why = NULL; + } + break; + + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: { + if (!EG(in_execution) && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + phpdbg_error("Not running"); + } + goto out; } - goto out; } } - phpdbg_destroy_input(&input TSRMLS_CC); - } while ((input = phpdbg_read_input(NULL TSRMLS_CC)) && (input->length > 0L)); + if (why) { + free(why); + why = NULL; + } - if (input && !input->length) - goto last; + yy_delete_buffer(state, scanner); + yylex_destroy(scanner); - } else { -last: - if (PHPDBG_G(lcmd)) { - ret = PHPDBG_G(lcmd)->handler( - &PHPDBG_G(lparam), input TSRMLS_CC); - goto out; - } + phpdbg_stack_free(&stack); + phpdbg_destroy_input(&input TSRMLS_CC); + + } while ((input = phpdbg_read_input(NULL TSRMLS_CC))); } out: - phpdbg_destroy_input(&input TSRMLS_CC); + if (input) { + phpdbg_stack_free(&stack); + phpdbg_destroy_input(&input TSRMLS_CC); + } + + if (why) { + free(why); + } if (EG(in_execution)) { phpdbg_restore_frame(TSRMLS_C); @@ -1075,6 +1086,8 @@ out: PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE; + phpdbg_print_changed_zvals(TSRMLS_C); + return ret; } /* }}} */ @@ -1214,7 +1227,7 @@ zend_vm_enter: ); \ } \ \ - do { \ +/* do { */\ switch (phpdbg_interactive(TSRMLS_C)) { \ case PHPDBG_LEAVE: \ case PHPDBG_FINISH: \ @@ -1223,7 +1236,7 @@ zend_vm_enter: goto next; \ } \ } \ - } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); \ +/* } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); */\ } while (0) /* allow conditional breakpoints and @@ -1285,22 +1298,30 @@ zend_vm_enter: phpdbg_print_opline_ex( execute_data, &vars, 0 TSRMLS_CC); + if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING && (PHPDBG_G(flags) & PHPDBG_STEP_OPCODE || execute_data->opline->lineno != PHPDBG_G(last_line))) { + PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING; + DO_INTERACTIVE(); + } + + /* check if some watchpoint was hit */ + { + if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) { + DO_INTERACTIVE(); + } + } + /* search for breakpoints */ { phpdbg_breakbase_t *brake; - if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) && - (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) { - phpdbg_hit_breakpoint( - brake, 1 TSRMLS_CC); + if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) + && (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC)) + && (brake->type != PHPDBG_BREAK_FILE || execute_data->opline->lineno != PHPDBG_G(last_line))) { + phpdbg_hit_breakpoint(brake, 1 TSRMLS_CC); DO_INTERACTIVE(); } } - if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) { - DO_INTERACTIVE(); - } - next: if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { phpdbg_writeln(EMPTY); @@ -1309,6 +1330,8 @@ next: DO_INTERACTIVE(); } + PHPDBG_G(last_line) = execute_data->opline->lineno; + PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); if (PHPDBG_G(vmret) > 0) { diff --git a/phpdbg_prompt.h b/phpdbg_prompt.h index 6807d88f41..ef648aabeb 100644 --- a/phpdbg_prompt.h +++ b/phpdbg_prompt.h @@ -30,11 +30,10 @@ void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */ /* {{{ phpdbg command handlers */ PHPDBG_COMMAND(exec); -PHPDBG_COMMAND(compile); PHPDBG_COMMAND(step); -PHPDBG_COMMAND(next); +PHPDBG_COMMAND(continue); PHPDBG_COMMAND(run); -PHPDBG_COMMAND(eval); +PHPDBG_COMMAND(ev); PHPDBG_COMMAND(until); PHPDBG_COMMAND(finish); PHPDBG_COMMAND(leave); @@ -47,13 +46,13 @@ PHPDBG_COMMAND(info); PHPDBG_COMMAND(clean); PHPDBG_COMMAND(clear); PHPDBG_COMMAND(help); -PHPDBG_COMMAND(quiet); -PHPDBG_COMMAND(aliases); -PHPDBG_COMMAND(shell); +PHPDBG_COMMAND(sh); PHPDBG_COMMAND(set); PHPDBG_COMMAND(source); +PHPDBG_COMMAND(export); PHPDBG_COMMAND(register); -PHPDBG_COMMAND(quit); /* }}} */ +PHPDBG_COMMAND(quit); +PHPDBG_COMMAND(watch); /* }}} */ /* {{{ prompt commands */ extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */ diff --git a/phpdbg_set.c b/phpdbg_set.c index 7c4da12a46..54269a8193 100644 --- a/phpdbg_set.c +++ b/phpdbg_set.c @@ -23,56 +23,52 @@ #include "phpdbg_set.h" #include "phpdbg_utils.h" #include "phpdbg_bp.h" +#include "phpdbg_prompt.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); -PHPDBG_SET(prompt) /* {{{ */ -{ - switch (param->type) { - case EMPTY_PARAM: - phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C)); - break; +#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s) \ + PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18]) - case STR_PARAM: - phpdbg_set_prompt(param->str TSRMLS_CC); - break; - - phpdbg_default_switch_case(); - } +const phpdbg_command_t phpdbg_set_commands[] = { + PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt []", 'p', set_prompt, NULL, "|s"), +#ifndef _WIN32 + PHPDBG_SET_COMMAND_D(color, "usage: set color ", 'c', set_color, NULL, "ss"), + PHPDBG_SET_COMMAND_D(colors, "usage: set colors []", 'C', set_colors, NULL, "|b"), +#endif + PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog []", 'O', set_oplog, NULL, "|s"), + PHPDBG_SET_COMMAND_D(break, "usage: set break id []", 'b', set_break, NULL, "l|b"), + PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks []", 'B', set_breaks, NULL, "|b"), + PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet []", 'q', set_quiet, NULL, "|b"), + PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping []", 's', set_stepping, NULL, "|s"), + PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount []", '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] "); + } - } 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 "); + "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 "); } -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 []"); + } + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_SET(refcount) /* {{{ */ +{ + if (!param || param->type == EMPTY_PARAM) { + phpdbg_writeln("Refcount %s", PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off"); + } else switch (param->type) { + case NUMERIC_PARAM: { + if (param->num) { + PHPDBG_G(flags) |= PHPDBG_SHOW_REFCOUNTS; + } else { + PHPDBG_G(flags) &= ~PHPDBG_SHOW_REFCOUNTS; + } + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ diff --git a/phpdbg_set.h b/phpdbg_set.h index 120aeb34f4..dea61ed382 100644 --- a/phpdbg_set.h +++ b/phpdbg_set.h @@ -32,16 +32,11 @@ PHPDBG_SET(colors); #endif PHPDBG_SET(oplog); PHPDBG_SET(break); +PHPDBG_SET(breaks); +PHPDBG_SET(quiet); +PHPDBG_SET(stepping); +PHPDBG_SET(refcount); -static const phpdbg_command_t phpdbg_set_commands[] = { - PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt ", 'p', set_prompt, NULL, 0), -#ifndef _WIN32 - PHPDBG_COMMAND_D_EX(color, "usage: set color ", 'c', set_color, NULL, 1), - PHPDBG_COMMAND_D_EX(colors, "usage: set colors ", 'C', set_colors, NULL, 1), -#endif - PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog ", 'O', set_oplog, NULL, 0), - PHPDBG_COMMAND_D_EX(break, "usage: set break [id] ", 'b', set_break, NULL, 0), - PHPDBG_END_COMMAND -}; +extern const phpdbg_command_t phpdbg_set_commands[]; #endif /* PHPDBG_SET_H */ diff --git a/phpdbg_utils.c b/phpdbg_utils.c index 1effcfccaf..c9b22a2039 100644 --- a/phpdbg_utils.c +++ b/phpdbg_utils.c @@ -30,6 +30,8 @@ #ifdef _WIN32 # include "win32/time.h" +#elif defined(HAVE_SYS_IOCTL_H) +# include "sys/ioctl.h" #endif ZEND_EXTERN_MODULE_GLOBALS(phpdbg); @@ -65,6 +67,14 @@ const static phpdbg_color_t colors[] = { PHPDBG_COLOR_END }; /* }}} */ +/* {{{ */ +const static phpdbg_element_t elements[] = { + PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT), + PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR), + PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE), + PHPDBG_ELEMENT_END +}; /* }}} */ + PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */ { if (!str) @@ -347,6 +357,21 @@ PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D) /* {{{ */ return colors; } /* }}} */ +PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC) { + const phpdbg_element_t *element = elements; + + while (element && element->name) { + if (len == element->name_length) { + if (strncasecmp(name, element->name, len) == SUCCESS) { + return element->id; + } + } + element++; + } + + return PHPDBG_COLOR_INVALID; +} + PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */ { /* free formatted prompt */ @@ -385,3 +410,39 @@ PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */ return PHPDBG_G(prompt)[1]; } /* }}} */ + +int phpdbg_rebuild_symtable(TSRMLS_D) { + if (!EG(active_op_array)) { + phpdbg_error("No active op array!"); + return FAILURE; + } + + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + + if (!EG(active_symbol_table)) { + phpdbg_error("No active symbol table!"); + return FAILURE; + } + } + + return SUCCESS; +} + +PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */ +{ + int columns; +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; +#elif defined(HAVE_SYS_IOCTL_H) + struct winsize w; + + columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 100; +#else + columns = 100; +#endif + return columns; +} /* }}} */ diff --git a/phpdbg_utils.h b/phpdbg_utils.h index c5164c3ac3..68ae7e44a3 100644 --- a/phpdbg_utils.h +++ b/phpdbg_utils.h @@ -85,12 +85,17 @@ PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...); {color, sizeof(color)-1, code} #define PHPDBG_COLOR_END \ {NULL, 0L, {0}} +#define PHPDBG_ELEMENT_LEN 3 +#define PHPDBG_ELEMENT_D(name, id) \ + {name, sizeof(name)-1, id} +#define PHPDBG_ELEMENT_END \ + {NULL, 0L, 0} #define PHPDBG_COLOR_INVALID -1 -#define PHPDBG_COLOR_PROMPT 0 -#define PHPDBG_COLOR_ERROR 1 -#define PHPDBG_COLOR_NOTICE 2 -#define PHPDBG_COLORS 3 +#define PHPDBG_COLOR_PROMPT 0 +#define PHPDBG_COLOR_ERROR 1 +#define PHPDBG_COLOR_NOTICE 2 +#define PHPDBG_COLORS 3 typedef struct _phpdbg_color_t { char *name; @@ -98,13 +103,25 @@ typedef struct _phpdbg_color_t { const char code[PHPDBG_COLOR_LEN]; } phpdbg_color_t; +typedef struct _phpdbg_element_t { + char *name; + size_t name_length; + int id; +} phpdbg_element_t; + PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC); PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC); PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC); -PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); /* }}} */ +PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); +PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC); /* }}} */ /* {{{ Prompt Management */ PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC); PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */ +/* {{{ Console Width */ +PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */ + +int phpdbg_rebuild_symtable(TSRMLS_D); + #endif /* PHPDBG_UTILS_H */ diff --git a/phpdbg_watch.c b/phpdbg_watch.c new file mode 100644 index 0000000000..a6bf6289bf --- /dev/null +++ b/phpdbg_watch.c @@ -0,0 +1,789 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "phpdbg.h" +#include "phpdbg_btree.h" +#include "phpdbg_watch.h" +#include "phpdbg_utils.h" +#ifndef _WIN32 +# include +# include +#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 index 0000000000..d00bcff77e --- /dev/null +++ b/phpdbg_watch.h @@ -0,0 +1,112 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#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 index 0000000000..b0cbdf267a --- /dev/null +++ b/phpdbg_win.c @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#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 index 0000000000..68c3052790 --- /dev/null +++ b/phpdbg_win.h @@ -0,0 +1,37 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHPDBG_WIN_H +#define PHPDBG_WIN_H + +#include "winbase.h" +#include "windows.h" +#include "excpt.h" + +#define PROT_READ 1 +#define PROT_WRITE 2 + +int mprotect(void *addr, size_t size, int protection); + +void phpdbg_win_set_mm_heap(zend_mm_heap *heap); + +int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp); + +#endif \ No newline at end of file diff --git a/test.php b/test.php index 5fdbcbe1a4..d93c81a89a 100644 --- 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);