summaryrefslogtreecommitdiff
path: root/sapi/phpdbg
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/phpdbg')
-rw-r--r--sapi/phpdbg/.gdbinit10
-rw-r--r--sapi/phpdbg/.gitignore6
-rw-r--r--sapi/phpdbg/.phpdbginit105
-rw-r--r--sapi/phpdbg/.travis.yml11
-rw-r--r--sapi/phpdbg/CREDITS2
-rw-r--r--sapi/phpdbg/Changelog.md52
-rw-r--r--sapi/phpdbg/Makefile.frag42
-rw-r--r--sapi/phpdbg/README.md83
-rw-r--r--sapi/phpdbg/config.m481
-rw-r--r--sapi/phpdbg/config.w3224
-rw-r--r--sapi/phpdbg/phpdbg.159
-rw-r--r--sapi/phpdbg/phpdbg.c1589
-rw-r--r--sapi/phpdbg/phpdbg.h298
-rwxr-xr-xsapi/phpdbg/phpdbg.init.d122
-rw-r--r--sapi/phpdbg/phpdbg_bp.c1623
-rw-r--r--sapi/phpdbg/phpdbg_bp.h159
-rw-r--r--sapi/phpdbg/phpdbg_break.c55
-rw-r--r--sapi/phpdbg/phpdbg_break.h37
-rw-r--r--sapi/phpdbg/phpdbg_btree.c221
-rw-r--r--sapi/phpdbg/phpdbg_btree.h65
-rw-r--r--sapi/phpdbg/phpdbg_cmd.c924
-rw-r--r--sapi/phpdbg/phpdbg_cmd.h184
-rw-r--r--sapi/phpdbg/phpdbg_eol.c172
-rw-r--r--sapi/phpdbg/phpdbg_eol.h46
-rw-r--r--sapi/phpdbg/phpdbg_frame.c252
-rw-r--r--sapi/phpdbg/phpdbg_frame.h30
-rw-r--r--sapi/phpdbg/phpdbg_help.c945
-rw-r--r--sapi/phpdbg/phpdbg_help.h48
-rw-r--r--sapi/phpdbg/phpdbg_info.c488
-rw-r--r--sapi/phpdbg/phpdbg_info.h41
-rw-r--r--sapi/phpdbg/phpdbg_io.c272
-rw-r--r--sapi/phpdbg/phpdbg_io.h34
-rw-r--r--sapi/phpdbg/phpdbg_lexer.c1519
-rw-r--r--sapi/phpdbg/phpdbg_lexer.h41
-rw-r--r--sapi/phpdbg/phpdbg_lexer.l184
-rw-r--r--sapi/phpdbg/phpdbg_list.c306
-rw-r--r--sapi/phpdbg/phpdbg_list.h54
-rw-r--r--sapi/phpdbg/phpdbg_opcode.c356
-rw-r--r--sapi/phpdbg/phpdbg_opcode.h31
-rw-r--r--sapi/phpdbg/phpdbg_out.c1305
-rw-r--r--sapi/phpdbg/phpdbg_out.h93
-rw-r--r--sapi/phpdbg/phpdbg_parser.c1951
-rw-r--r--sapi/phpdbg/phpdbg_parser.h109
-rw-r--r--sapi/phpdbg/phpdbg_parser.y191
-rw-r--r--sapi/phpdbg/phpdbg_print.c264
-rw-r--r--sapi/phpdbg/phpdbg_print.h40
-rw-r--r--sapi/phpdbg/phpdbg_prompt.c1617
-rw-r--r--sapi/phpdbg/phpdbg_prompt.h72
-rw-r--r--sapi/phpdbg/phpdbg_rinit_hook.c101
-rw-r--r--sapi/phpdbg/phpdbg_rinit_hook.h41
-rw-r--r--sapi/phpdbg/phpdbg_set.c250
-rw-r--r--sapi/phpdbg/phpdbg_set.h42
-rw-r--r--sapi/phpdbg/phpdbg_sigio_win32.c120
-rw-r--r--sapi/phpdbg/phpdbg_sigio_win32.h42
-rw-r--r--sapi/phpdbg/phpdbg_sigsafe.c111
-rw-r--r--sapi/phpdbg/phpdbg_sigsafe.h25
-rw-r--r--sapi/phpdbg/phpdbg_utils.c642
-rw-r--r--sapi/phpdbg/phpdbg_utils.h140
-rw-r--r--sapi/phpdbg/phpdbg_wait.c419
-rw-r--r--sapi/phpdbg/phpdbg_wait.h29
-rw-r--r--sapi/phpdbg/phpdbg_watch.c715
-rw-r--r--sapi/phpdbg/phpdbg_watch.h112
-rw-r--r--sapi/phpdbg/phpdbg_webdata_transfer.c185
-rw-r--r--sapi/phpdbg/phpdbg_webdata_transfer.h27
-rw-r--r--sapi/phpdbg/phpdbg_win.c42
-rw-r--r--sapi/phpdbg/phpdbg_win.h37
-rw-r--r--sapi/phpdbg/test.php87
-rw-r--r--sapi/phpdbg/tests/commands/0001_basic.test8
-rw-r--r--sapi/phpdbg/tests/commands/0002_set.test21
-rw-r--r--sapi/phpdbg/tests/commands/0101_info.test19
-rw-r--r--sapi/phpdbg/tests/commands/0102_print.test27
-rw-r--r--sapi/phpdbg/tests/commands/0103_register.test28
-rw-r--r--sapi/phpdbg/tests/commands/0104_clean.test15
-rw-r--r--sapi/phpdbg/tests/commands/0105_clear.test18
-rw-r--r--sapi/phpdbg/tests/commands/0106_compile.test18
-rw-r--r--sapi/phpdbg/tests/run-tests.php597
-rwxr-xr-xsapi/phpdbg/travis/ci.sh11
-rw-r--r--sapi/phpdbg/web-bootstrap.php64
-rw-r--r--sapi/phpdbg/xml.md759
-rw-r--r--sapi/phpdbg/zend_mm_structs.h102
80 files changed, 21067 insertions, 0 deletions
diff --git a/sapi/phpdbg/.gdbinit b/sapi/phpdbg/.gdbinit
new file mode 100644
index 0000000000..401a4bb88c
--- /dev/null
+++ b/sapi/phpdbg/.gdbinit
@@ -0,0 +1,10 @@
+define ____phpdbg_globals
+ if basic_functions_module.zts
+ if !$tsrm_ls
+ set $tsrm_ls = ts_resource_ex(0, 0)
+ end
+ set $phpdbg = ((zend_phpdbg_globals*) (*((void ***) $tsrm_ls))[phpdbg_globals_id-1])
+ else
+ set $phpdbg = phpdbg_globals
+ end
+end
diff --git a/sapi/phpdbg/.gitignore b/sapi/phpdbg/.gitignore
new file mode 100644
index 0000000000..51165dab2f
--- /dev/null
+++ b/sapi/phpdbg/.gitignore
@@ -0,0 +1,6 @@
+.libs/
+phpdbg
+*.lo
+*.o
+*.output
+build
diff --git a/sapi/phpdbg/.phpdbginit b/sapi/phpdbg/.phpdbginit
new file mode 100644
index 0000000000..1ad35218ed
--- /dev/null
+++ b/sapi/phpdbg/.phpdbginit
@@ -0,0 +1,105 @@
+##########################################################
+# .phpdbginit
+#
+# Lines starting with # are ignored
+# Code must start and end with <: and :> respectively
+##########################################################
+# Place initialization commands one per line
+##########################################################
+# exec sapi/phpdbg/test.php
+# set color prompt white-bold
+# set color notice green
+# set color error red
+
+##########################################################
+# Embedding code in .phpdbginit
+##########################################################
+<:
+/*
+* This embedded PHP is executed at init time
+*/
+
+/*
+* Functions defined and registered by init
+* will persist across cleans
+*/
+
+/*
+function my_debugging_function()
+{
+ var_dump(func_get_args());
+}
+*/
+
+/* phpdbg_break(PHPDBG_METHOD, "phpdbg::method"); */
+/* phpdbg_break(PHPDBG_FUNC, "my_global_function"); */
+/* phpdbg_break(PHPDBG_FILE, "/path/to/file.php:10"); */
+
+/*
+ If readline is loaded, you might want to setup completion:
+*/
+if (function_exists('readline_completion_function')) {
+ readline_completion_function(function(){
+ return array_merge(
+ get_defined_functions()['user'],
+ array_keys(get_defined_constants())
+ );
+ });
+}
+
+/*
+ Setting argv made trivial ...
+
+ argv 1 2 3 4
+ ^ set argv for next execution
+
+ argv
+ ^ unset argv for next execution
+
+*/
+function argv()
+{
+ $argv = func_get_args();
+
+ if (!$argv) {
+ $_SERVER['argv'] = array();
+ $_SERVER['argc'] = 0;
+ return;
+ }
+
+ $_SERVER['argv'] = array_merge
+ (
+ array("phpdbg"),
+ $argv
+ );
+ $_SERVER['argc'] = count($_SERVER['argv']);
+
+ return $_SERVER['argv'];
+}
+:>
+##########################################################
+# Now carry on initializing phpdbg ...
+##########################################################
+# R my_debugging_function
+# R argv
+
+##########################################################
+# PHP has many functions that might be useful
+# ... you choose ...
+##########################################################
+# R touch
+# R unlink
+# R scandir
+# R glob
+
+##########################################################
+# Remember: *you have access to the shell*
+##########################################################
+# The output of registered function calls is not,
+# by default, very pretty (unless you implement
+# and register a new implementation for phpdbg)
+# The output of shell commands will usually be more
+# readable on the console
+##########################################################
+# TLDR; if you have a good shell, use it ...
+##########################################################
diff --git a/sapi/phpdbg/.travis.yml b/sapi/phpdbg/.travis.yml
new file mode 100644
index 0000000000..2e777fbe13
--- /dev/null
+++ b/sapi/phpdbg/.travis.yml
@@ -0,0 +1,11 @@
+language: c
+
+env:
+- PHP="PHP-5.4"
+- PHP="PHP-5.5"
+- PHP="PHP-5.6"
+
+before_script: ./travis/ci.sh
+
+script:
+- ./php-src/sapi/cli/php php-src/sapi/phpdbg/tests/run-tests.php -diff2stdout --phpdbg php-src/sapi/phpdbg/phpdbg
diff --git a/sapi/phpdbg/CREDITS b/sapi/phpdbg/CREDITS
new file mode 100644
index 0000000000..036bafe1db
--- /dev/null
+++ b/sapi/phpdbg/CREDITS
@@ -0,0 +1,2 @@
+phpdbg
+Felipe Pena, Joe Watkins, Bob Weinand
diff --git a/sapi/phpdbg/Changelog.md b/sapi/phpdbg/Changelog.md
new file mode 100644
index 0000000000..c5d8b51514
--- /dev/null
+++ b/sapi/phpdbg/Changelog.md
@@ -0,0 +1,52 @@
+ChangeLog for phpdbg
+====================
+
+Version 0.3.0 2013-00-00
+------------------------
+
+1. Added ability to disable an enable a single breakpoint
+2. Added ability to override SAPI name
+3. Added extended conditional breakpoint support "break at"
+4. Fix loading of zend extnsions with -z
+5. Fix crash when loading .phpdbginit with command line switch
+6. Fix crash on startup errors
+7. Added init.d for remote console (redhat)
+8. Added phpdbg_exec userland function
+9. Added testing facilities
+10. Added break on n-th opline support
+11. Improved trace output
+
+Version 0.2.0 2013-11-31
+------------------------
+
+1. Added "break delete <id>" command
+2. Added "break opcode <opcode>" command
+3. Added "set" command - control prompt and console colors
+4. .phpdbginit now searched in (additional) ini dirs
+5. Added source command - load additional .phpdbginit script during session
+6. Added remote console mode
+7. Added info memory command
+
+Version 0.1.0 2013-11-23
+------------------------
+
+1. New commands:
+ - until (continue until the current line is executed)
+ - frame (switch to a frame in the current stack for inspection)
+ - info (quick access to useful information on the console)
+ - finish (continue until the current function has returned)
+ - leave (continue until the current function is returning)
+ - shell (shell a command)
+ - register (register a function for use as a command)
+2. Added printers for class and method
+3. Make uniform commands and aliases where possible
+4. Include all alias information and sub-command information in help
+5. Added signal handling to break execution (ctrl-c)
+6. Fixed #13 (Output Buffering Control seems fail)
+7. Fixed #14 (Fixed typo in Makefile.frag)
+
+
+Version 0.0.1 2013-11-15
+------------------------
+
+1. Initial features
diff --git a/sapi/phpdbg/Makefile.frag b/sapi/phpdbg/Makefile.frag
new file mode 100644
index 0000000000..36c7512d69
--- /dev/null
+++ b/sapi/phpdbg/Makefile.frag
@@ -0,0 +1,42 @@
+phpdbg: $(BUILD_BINARY)
+
+phpdbg-shared: $(BUILD_SHARED)
+
+$(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
+ $(BUILD_PHPDBG_SHARED)
+
+$(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
+ $(BUILD_PHPDBG)
+
+%.c: %.y
+%.c: %.l
+
+$(builddir)/phpdbg_lexer.lo: $(srcdir)/phpdbg_parser.h
+
+$(srcdir)/phpdbg_lexer.c: $(srcdir)/phpdbg_lexer.l
+ @(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date -cbdFo $(srcdir)/phpdbg_lexer.c $(srcdir)/phpdbg_lexer.l)
+
+$(srcdir)/phpdbg_parser.h: $(srcdir)/phpdbg_parser.c
+$(srcdir)/phpdbg_parser.c: $(srcdir)/phpdbg_parser.y
+ @$(YACC) -p phpdbg_ -v -d $(srcdir)/phpdbg_parser.y -o $@
+
+install-phpdbg: $(BUILD_BINARY)
+ @echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/run
+ @$(INSTALL) -m 0755 $(BUILD_BINARY) $(INSTALL_ROOT)$(bindir)/$(program_prefix)phpdbg$(program_suffix)$(EXEEXT)
+ @echo "Installing phpdbg man page: $(INSTALL_ROOT)$(mandir)/man1/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man1
+ @$(INSTALL_DATA) $(srcdir)/phpdbg.1 $(INSTALL_ROOT)$(mandir)/man1/$(program_prefix)phpdbg$(program_suffix).1
+
+clean-phpdbg:
+ @echo "Cleaning phpdbg object files ..."
+ find sapi/phpdbg/ -name *.lo -o -name *.o | xargs rm -f
+
+test-phpdbg:
+ @echo "Running phpdbg tests ..."
+ @$(top_builddir)/sapi/cli/php sapi/phpdbg/tests/run-tests.php --phpdbg sapi/phpdbg/phpdbg
+
+.PHONY: clean-phpdbg test-phpdbg
+
diff --git a/sapi/phpdbg/README.md b/sapi/phpdbg/README.md
new file mode 100644
index 0000000000..a2a84deb7b
--- /dev/null
+++ b/sapi/phpdbg/README.md
@@ -0,0 +1,83 @@
+The interactive PHP debugger
+============================
+
+Implemented as a SAPI module, phpdbg can exert complete control over the environment without impacting the functionality or performance of your code.
+
+phpdbg aims to be a lightweight, powerful, easy to use debugging platform for PHP 5.4+
+
+[![phpdbg on travis-ci](https://travis-ci.org/krakjoe/phpdbg.png?branch=master)](https://travis-ci.org/krakjoe/phpdbg)
+
+Features
+========
+
+ - Stepthrough Debugging
+ - Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
+ - Easy Access to PHP with built-in eval()
+ - Easy Access to Currently Executing Code
+ - Userland API
+ - SAPI Agnostic - Easily Integrated
+ - PHP Configuration File Support
+ - JIT Super Globals - Set Your Own!!
+ - Optional readline Support - Comfortable Terminal Operation
+ - Remote Debugging Support - Bundled Java GUI
+ - Easy Operation - See Help :)
+
+Planned
+=======
+
+ - Improve Everything :)
+
+Installation
+============
+
+To install **phpdbg**, you must compile the source against your PHP installation sources, and enable the SAPI with the configure command.
+
+```
+cd /usr/src/php-src/sapi
+git clone https://github.com/krakjoe/phpdbg
+cd ../
+./buildconf --force
+./configure --enable-phpdbg
+make -j8
+make install-phpdbg
+```
+
+Where the source directory has been used previously to build PHP, there exists a file named *config.nice* which can be used to invoke configure with the same
+parameters as were used by the last execution of *configure*.
+
+**Note:** PHP must be configured with the switch --with-readline for phpdbg to support history, autocompletion, tab-listing etc.
+
+Command Line Options
+====================
+
+The following switches are implemented (just like cli SAPI):
+
+ - -n ignore php ini
+ - -c search for php ini in path
+ - -z load zend extension
+ - -d define php ini entry
+
+The following switches change the default behaviour of phpdbg:
+
+ - -v disables quietness
+ - -s enabled stepping
+ - -e sets execution context
+ - -b boring - disables use of colour on the console
+ - -I ignore .phpdbginit (default init file)
+ - -i override .phpgdbinit location (implies -I)
+ - -O set oplog output file
+ - -q do not print banner on startup
+ - -r jump straight to run
+ - -E enable step through eval()
+ - -l listen ports for remote mode
+ - -a listen address for remote mode
+ - -S override SAPI name
+
+**Note:** Passing -rr will cause phpdbg to quit after execution, rather than returning to the console.
+
+Getting Started
+===============
+
+See the website for tutorials/documentation
+
+http://phpdbg.com
diff --git a/sapi/phpdbg/config.m4 b/sapi/phpdbg/config.m4
new file mode 100644
index 0000000000..528abb5822
--- /dev/null
+++ b/sapi/phpdbg/config.m4
@@ -0,0 +1,81 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(phpdbg, for phpdbg support,
+[ --enable-phpdbg Build phpdbg], no, no)
+
+PHP_ARG_ENABLE(phpdbg-webhelper, for phpdbg web SAPI support,
+[ --enable-phpdbg-webhelper Build phpdbg web SAPI support], yes, yes)
+
+PHP_ARG_ENABLE(phpdbg-debug, for phpdbg debug build,
+[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
+
+if test "$BUILD_PHPDBG" == "" && test "$PHP_PHPDBG" != "no"; then
+ AC_HEADER_TIOCGWINSZ
+ AC_DEFINE(HAVE_PHPDBG, 1, [ ])
+
+ if test "$PHP_PHPDBG_DEBUG" != "no"; then
+ AC_DEFINE(PHPDBG_DEBUG, 1, [ ])
+ else
+ AC_DEFINE(PHPDBG_DEBUG, 0, [ ])
+ fi
+
+ if test "$PHP_PHPDBG_WEBHELPER" != "no"; then
+ if ! test -d ext/phpdbg_webhelper; then
+ ln -s ../sapi/phpdbg ext/phpdbg_webhelper
+ fi
+ if test "$PHP_JSON" != "no"; then
+ PHP_NEW_EXTENSION(phpdbg_webhelper, phpdbg_rinit_hook.c phpdbg_webdata_transfer.c, $ext_shared)
+ else
+ AC_MSG_ERROR(Webhelper extension of phpdbg needs json enabled)
+ fi
+ fi
+
+ PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
+ 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 phpdbg_sigsafe.c phpdbg_wait.c phpdbg_io.c phpdbg_eol.c phpdbg_out.c"
+
+ if test "$PHP_READLINE" != "no" -o "$PHP_LIBEDIT" != "no"; then
+ PHPDBG_EXTRA_LIBS="$PHP_READLINE_LIBS"
+ 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], [$abs_srcdir/sapi/phpdbg], [$abs_builddir/sapi/phpdbg])
+ 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) \
+ \$(PHP_BINARY_OBJS) \
+ \$(PHP_PHPDBG_OBJS) \
+ \$(EXTRA_LIBS) \
+ \$(PHPDBG_EXTRA_LIBS) \
+ \$(ZEND_EXTRA_LIBS) \
+ -o \$(BUILD_BINARY)"
+
+ BUILD_PHPDBG_SHARED="\$(LIBTOOL) --mode=link \
+ \$(CC) -shared -Wl,-soname,libphpdbg.so -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
+ \$(PHP_GLOBAL_OBJS) \
+ \$(PHP_BINARY_OBJS) \
+ \$(PHP_PHPDBG_OBJS) \
+ \$(EXTRA_LIBS) \
+ \$(PHPDBG_EXTRA_LIBS) \
+ \$(ZEND_EXTRA_LIBS) \
+ \-DPHPDBG_SHARED \
+ -o \$(BUILD_SHARED)"
+
+ PHP_SUBST(BUILD_BINARY)
+ PHP_SUBST(BUILD_SHARED)
+ PHP_SUBST(BUILD_PHPDBG)
+ PHP_SUBST(BUILD_PHPDBG_SHARED)
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/phpdbg/config.w32 b/sapi/phpdbg/config.w32
new file mode 100644
index 0000000000..6f0bd8f811
--- /dev/null
+++ b/sapi/phpdbg/config.w32
@@ -0,0 +1,24 @@
+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_watch.c phpdbg_win.c phpdbg_btree.c '+
+ 'phpdbg_parser.c phpdbg_lexer.c phpdbg_sigsafe.c phpdbg_wait.c phpdbg_io.c ' +
+ 'phpdbg_sigio_win32.c phpdbg_eol.c phpdbg_out.c';
+PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll';
+PHPDBG_EXE='phpdbg.exe';
+
+if (PHP_PHPDBG == "yes") {
+ SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE);
+ ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib");
+ ADD_FLAG("CFLAGS_PHPDBG", "/D YY_NO_UNISTD_H");
+ ADD_FLAG("LDFLAGS_PHPDBG", "/stack:8388608");
+}
+
+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");
+ ADD_FLAG("CFLAGS_PHPDBGS", "/D YY_NO_UNISTD_H");
+}
+
diff --git a/sapi/phpdbg/phpdbg.1 b/sapi/phpdbg/phpdbg.1
new file mode 100644
index 0000000000..5e4d144c83
--- /dev/null
+++ b/sapi/phpdbg/phpdbg.1
@@ -0,0 +1,59 @@
+.TH PHPDBG 1
+.SH NAME
+phpdbg \- The interactive PHP debugger
+.SH SYNOPSIS
+.B phpdbg
+[OPTION]
+[\fB\-e\fIFILE\fR]
+.SH DESCRIPTION
+.B phpdbg
+a lightweight, powerful, easy to use debugging platform for PHP5.
+.SH OPTIONS
+The following switches are implemented (just like cli SAPI):
+.TP
+.BR \-n
+No \fBphp.ini\fR file will be used
+.TP
+.BR \-c \fIpath\fB|\fIfile\fR
+Look for \fBphp.ini\fR file in the directory \fIpath\fR or use the specified \fIfile\fR
+.TP
+.BR \-z \fIfile\fR
+Load Zend extension \fIfile\fR
+.TP
+.BR \-d \fIfoo\fB[=\fIbar\fB]\fR
+Define INI entry \fIfoo\fR with value \fIbar\fR
+.PP The following switches change the default behaviour of phpdbg:
+.TP
+.BR \-v
+Disables quietness
+.TP
+.BR \-s
+Enabled stepping
+.TP
+.BR -e \fIfile\fR
+Sets execution context
+.TP
+.BR \-b
+Disables use of colour on the console
+.TP
+.BR \-I
+Ignore .phpdbginit (default init file)
+.TP
+.BR \-i \fIpath\fB|\ffile\fR
+Override .phpgdbinit location (implies -I)
+.TP
+.BR \-O \fIfile\fR
+Set oplog output to \fIfile\fR
+.TP
+.BR \-q
+Do not print banner on startup
+.TP
+.BR \-r
+Jump straight to run
+.TP
+.BR \-E
+Enable step through eval()
+.SH NOTES
+Passing -rr will cause phpdbg to quit after execution, rather than returning to the console
+.SH AUTHOR
+Written by Felipe Pena, Joe Watkins and Bob Weinand, formatted by Ondřej Surý for Debian project.
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c
new file mode 100644
index 0000000000..aaf1e5f406
--- /dev/null
+++ b/sapi/phpdbg/phpdbg.c
@@ -0,0 +1,1589 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#if !defined(ZEND_SIGNALS) || defined(_WIN32)
+# include <signal.h>
+#endif
+#include "phpdbg.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_break.h"
+#include "phpdbg_list.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_set.h"
+#include "phpdbg_io.h"
+#include "zend_alloc.h"
+#include "phpdbg_eol.h"
+
+/* {{{ remote console headers */
+#ifndef _WIN32
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/poll.h>
+# include <netinet/in.h>
+# include <unistd.h>
+# include <arpa/inet.h>
+#endif /* }}} */
+
+ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
+
+static PHP_INI_MH(OnUpdateEol)
+{
+ if (!new_value) {
+ return FAILURE;
+ }
+
+ return phpdbg_eol_global_update(new_value TSRMLS_CC);
+}
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, socket_path, zend_phpdbg_globals, phpdbg_globals)
+ STD_PHP_INI_ENTRY("phpdbg.eol", "2", PHP_INI_ALL, OnUpdateEol, socket_path, zend_phpdbg_globals, phpdbg_globals)
+PHP_INI_END()
+
+static zend_bool phpdbg_booted = 0;
+
+#if PHP_VERSION_ID >= 50500
+void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC);
+#else
+void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC);
+#endif
+
+static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
+{
+ pg->prompt[0] = NULL;
+ pg->prompt[1] = NULL;
+
+ pg->colors[0] = NULL;
+ pg->colors[1] = NULL;
+ pg->colors[2] = NULL;
+
+ pg->exec = NULL;
+ pg->exec_len = 0;
+ pg->buffer = NULL;
+ pg->last_was_newline = 1;
+ pg->ops = NULL;
+ pg->vmret = 0;
+ pg->bp_count = 0;
+ pg->flags = PHPDBG_DEFAULT_FLAGS;
+ pg->oplog = NULL;
+ memset(pg->io, 0, sizeof(pg->io));
+ pg->frame.num = 0;
+ pg->sapi_name_ptr = NULL;
+ pg->socket_fd = -1;
+ pg->socket_server_fd = -1;
+
+ pg->req_id = 0;
+ pg->err_buf.active = 0;
+ pg->err_buf.type = 0;
+
+ pg->input_buflen = 0;
+ pg->sigsafe_mem.mem = NULL;
+ pg->sigsegv_bailout = NULL;
+
+#ifdef PHP_WIN32
+ pg->sigio_watcher_thread = INVALID_HANDLE_VALUE;
+ memset(&pg->swd, 0, sizeof(struct win32_sigio_watcher_data));
+#endif
+
+ pg->eol = PHPDBG_EOL_LF;
+} /* }}} */
+
+static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
+{
+ ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
+ REGISTER_INI_ENTRIES();
+
+#if PHP_VERSION_ID >= 50500
+ zend_execute_old = zend_execute_ex;
+ zend_execute_ex = phpdbg_execute_ex;
+#else
+ zend_execute_old = zend_execute;
+ zend_execute = phpdbg_execute_ex;
+#endif
+
+ REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
+
+ return SUCCESS;
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */
+{
+ zend_hash_destroy((HashTable*)brake);
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */
+{
+ efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol);
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */
+{
+ efree((char*)((phpdbg_breakop_t*)brake)->name);
+} /* }}} */
+
+
+static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */
+{
+ zend_hash_destroy((HashTable*)brake);
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */
+{
+ phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data;
+
+ if (brake) {
+ if (brake->ops) {
+ TSRMLS_FETCH();
+
+ destroy_op_array(
+ brake->ops TSRMLS_CC);
+ efree(brake->ops);
+ }
+ efree((char*)brake->code);
+ }
+} /* }}} */
+
+static void php_phpdbg_destroy_registered(void *data) /* {{{ */
+{
+ zend_function *function = (zend_function*) data;
+ TSRMLS_FETCH();
+
+ destroy_zend_function(
+ 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);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
+ 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);
+
+ zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
+
+ return SUCCESS;
+} /* }}} */
+
+static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
+{
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
+ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+ 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(file_sources));
+ 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;
+ }
+
+ if (PHPDBG_G(prompt)[0]) {
+ free(PHPDBG_G(prompt)[0]);
+ }
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ }
+
+ PHPDBG_G(prompt)[0] = NULL;
+ PHPDBG_G(prompt)[1] = NULL;
+
+ if (PHPDBG_G(oplog)) {
+ fclose(
+ PHPDBG_G(oplog));
+ PHPDBG_G(oplog) = NULL;
+ }
+
+ if (PHPDBG_G(ops)) {
+ destroy_op_array(PHPDBG_G(ops) TSRMLS_CC);
+ efree(PHPDBG_G(ops));
+ PHPDBG_G(ops) = NULL;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+/* {{{ 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 request to set the context fails, boolean false is returned, and an E_WARNING raised */
+static PHP_FUNCTION(phpdbg_exec)
+{
+ char *exec = NULL;
+ 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)) {
+ ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1);
+ efree(PHPDBG_G(exec));
+ result = 0;
+ }
+
+ PHPDBG_G(exec) = estrndup(exec, exec_len);
+ PHPDBG_G(exec_len) = exec_len;
+
+ if (result)
+ ZVAL_BOOL(return_value, 1);
+ } else {
+ zend_error(
+ E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec);
+ ZVAL_BOOL(return_value, 0);
+ }
+ } else {
+ zend_error(
+ E_WARNING, "Failed to set execution context (%s) the file does not exist", exec);
+
+ ZVAL_BOOL(return_value, 0);
+ }
+ }
+} /* }}} */
+
+/* {{{ proto void phpdbg_break([integer type, string expression])
+ instructs phpdbg to insert a breakpoint at the next opcode */
+static PHP_FUNCTION(phpdbg_break)
+{
+ if (ZEND_NUM_ARGS() > 0) {
+ long type = 0;
+ char *expr = NULL;
+ int expr_len = 0;
+ phpdbg_param_t param;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) {
+ return;
+ }
+
+ phpdbg_parse_param(expr, expr_len, &param TSRMLS_CC);
+ phpdbg_do_break(&param TSRMLS_CC);
+ phpdbg_clear_param(&param TSRMLS_CC);
+
+ } else if (EG(current_execute_data) && EG(active_op_array)) {
+ zend_ulong opline_num = (EG(current_execute_data)->opline -
+ EG(active_op_array)->opcodes);
+
+ phpdbg_set_breakpoint_opline_ex(
+ &EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC);
+ }
+} /* }}} */
+
+/* {{{ proto void phpdbg_clear(void)
+ instructs phpdbg to clear breakpoints */
+static PHP_FUNCTION(phpdbg_clear)
+{
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+} /* }}} */
+
+/* {{{ proto void phpdbg_color(integer element, string color) */
+static PHP_FUNCTION(phpdbg_color)
+{
+ long element = 0L;
+ char *color = NULL;
+ size_t color_len = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) {
+ return;
+ }
+
+ switch (element) {
+ case PHPDBG_COLOR_NOTICE:
+ case PHPDBG_COLOR_ERROR:
+ case PHPDBG_COLOR_PROMPT:
+ phpdbg_set_color_ex(element, color, color_len TSRMLS_CC);
+ break;
+
+ default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant");
+ }
+} /* }}} */
+
+/* {{{ proto void phpdbg_prompt(string prompt) */
+static PHP_FUNCTION(phpdbg_prompt)
+{
+ char *prompt = NULL;
+ size_t prompt_len = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) {
+ return;
+ }
+
+ phpdbg_set_prompt(prompt TSRMLS_CC);
+} /* }}} */
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, type)
+ ZEND_ARG_INFO(0, expression)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, element)
+ ZEND_ARG_INFO(0, color)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0)
+ ZEND_ARG_INFO(0, context)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+zend_function_entry phpdbg_user_functions[] = {
+ PHP_FE(phpdbg_clear, phpdbg_clear_arginfo)
+ PHP_FE(phpdbg_break, phpdbg_break_arginfo)
+ PHP_FE(phpdbg_exec, phpdbg_exec_arginfo)
+ PHP_FE(phpdbg_color, phpdbg_color_arginfo)
+ PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo)
+#ifdef PHP_FE_END
+ PHP_FE_END
+#else
+ {NULL,NULL,NULL}
+#endif
+};
+
+static zend_module_entry sapi_phpdbg_module_entry = {
+ STANDARD_MODULE_HEADER,
+ PHPDBG_NAME,
+ phpdbg_user_functions,
+ PHP_MINIT(phpdbg),
+ NULL,
+ PHP_RINIT(phpdbg),
+ PHP_RSHUTDOWN(phpdbg),
+ NULL,
+ PHPDBG_VERSION,
+ STANDARD_MODULE_PROPERTIES
+};
+
+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;
+} /* }}} */
+
+static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */
+{
+ return NULL;
+} /* }}} */
+
+static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */
+{
+ return 0;
+}
+/* }}} */
+
+static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */
+{
+ /* We do nothing here, this function is needed to prevent that the fallback
+ * header handling is called. */
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */
+{
+}
+/* }}} */
+
+static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */
+{
+ /*
+ * We must not request TSRM before being boot
+ */
+ if (phpdbg_booted) {
+ if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) {
+ phpdbg_error("eval", "msg=\"%s\"", "%s", message);
+ return;
+ }
+
+ phpdbg_error("php", "msg=\"%s\"", "%s", message);
+
+ switch (PG(last_error_type)) {
+ case E_ERROR:
+ case E_CORE_ERROR:
+ case E_COMPILE_ERROR:
+ case E_USER_ERROR:
+ case E_PARSE:
+ case E_RECOVERABLE_ERROR:
+ phpdbg_list_file(zend_get_executed_filename(TSRMLS_C), 3, zend_get_executed_lineno(TSRMLS_C)-1, zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
+
+ do {
+ switch (phpdbg_interactive(1 TSRMLS_CC)) {
+ case PHPDBG_LEAVE:
+ case PHPDBG_FINISH:
+ case PHPDBG_UNTIL:
+ case PHPDBG_NEXT:
+ return;
+ }
+ } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
+
+ }
+ } else fprintf(stdout, "%s\n", message);
+}
+/* }}} */
+
+static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */
+{
+ fflush(stdout);
+ if(SG(request_info).argv0) {
+ free(SG(request_info).argv0);
+ SG(request_info).argv0 = NULL;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {{{ */
+{
+ unsigned int len;
+ char *docroot = "";
+
+ /* In phpdbg mode, we consider the environment to be a part of the server variables
+ */
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ if (PHPDBG_G(exec)) {
+ len = PHPDBG_G(exec_len);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("PHP_SELF", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("SCRIPT_NAME", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+
+ if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED",
+ &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
+ php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec),
+ track_vars_array TSRMLS_CC);
+ }
+ }
+
+ /* any old docroot will doo */
+ len = 0U;
+ if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT",
+ &docroot, len, &len TSRMLS_CC)) {
+ php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC);
+ }
+}
+/* }}} */
+
+static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */
+{
+ if (PHPDBG_G(socket_fd) != -1 && !(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
+ send(PHPDBG_G(socket_fd), message, length, 0);
+ }
+ return phpdbg_script(P_STDOUT, "%.*s", length, message);
+} /* }}} */
+
+/* beginning of struct, see main/streams/plain_wrapper.c line 111 */
+typedef struct {
+ FILE *file;
+ int fd;
+} php_stdio_stream_data;
+
+static size_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) {
+ php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
+
+ while (data->fd >= 0) {
+ struct stat stat[3];
+ memset(stat, 0, sizeof(stat));
+ if (((fstat(fileno(stderr), &stat[2]) < 0) & (fstat(fileno(stdout), &stat[0]) < 0)) | (fstat(data->fd, &stat[1]) < 0)) {
+ break;
+ }
+
+ if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) {
+ phpdbg_script(P_STDOUT, "%.*s", (int) count, buf);
+ return count;
+ }
+ if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) {
+ phpdbg_script(P_STDERR, "%.*s", (int) count, buf);
+ return count;
+ }
+ break;
+ }
+
+ return PHPDBG_G(php_stdiop_write)(stream, buf, count TSRMLS_CC);
+}
+
+#if PHP_VERSION_ID >= 50700
+static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */
+{
+#else
+static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
+{
+ TSRMLS_FETCH();
+#endif
+
+ if (!phpdbg_active_sigsafe_mem(TSRMLS_C)) {
+ fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
+ }
+} /* }}} */
+
+/* copied from sapi/cli/php_cli.c cli_register_file_handles */
+static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */
+{
+ zval *zin, *zout, *zerr;
+ php_stream *s_in, *s_out, *s_err;
+ php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
+ zend_constant ic, oc, ec;
+
+ MAKE_STD_ZVAL(zin);
+ MAKE_STD_ZVAL(zout);
+ MAKE_STD_ZVAL(zerr);
+
+ s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in);
+ s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
+ s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
+
+ if (s_in==NULL || s_out==NULL || s_err==NULL) {
+ FREE_ZVAL(zin);
+ FREE_ZVAL(zout);
+ FREE_ZVAL(zerr);
+ if (s_in) php_stream_close(s_in);
+ if (s_out) php_stream_close(s_out);
+ if (s_err) php_stream_close(s_err);
+ return;
+ }
+
+#if PHP_DEBUG
+ /* do not close stdout and stderr */
+ s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+ s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+#endif
+
+ php_stream_to_zval(s_in, zin);
+ php_stream_to_zval(s_out, zout);
+ php_stream_to_zval(s_err, zerr);
+
+ ic.value = *zin;
+ ic.flags = CONST_CS;
+ ic.name = zend_strndup(ZEND_STRL("STDIN"));
+ ic.name_len = sizeof("STDIN");
+ ic.module_number = 0;
+ zend_register_constant(&ic TSRMLS_CC);
+
+ oc.value = *zout;
+ oc.flags = CONST_CS;
+ oc.name = zend_strndup(ZEND_STRL("STDOUT"));
+ oc.name_len = sizeof("STDOUT");
+ oc.module_number = 0;
+ zend_register_constant(&oc TSRMLS_CC);
+
+ ec.value = *zerr;
+ ec.flags = CONST_CS;
+ ec.name = zend_strndup(ZEND_STRL("STDERR"));
+ ec.name_len = sizeof("STDERR");
+ ec.module_number = 0;
+ zend_register_constant(&ec TSRMLS_CC);
+
+ FREE_ZVAL(zin);
+ FREE_ZVAL(zout);
+ FREE_ZVAL(zerr);
+}
+/* }}} */
+
+/* {{{ sapi_module_struct phpdbg_sapi_module
+*/
+static sapi_module_struct phpdbg_sapi_module = {
+ "phpdbg", /* name */
+ "phpdbg", /* pretty name */
+
+ php_sapi_phpdbg_module_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ php_sapi_phpdbg_deactivate, /* deactivate */
+
+ php_sapi_phpdbg_ub_write, /* unbuffered write */
+ php_sapi_phpdbg_flush, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ php_sapi_phpdbg_header_handler, /* header handler */
+ php_sapi_phpdbg_send_headers, /* send headers handler */
+ php_sapi_phpdbg_send_header, /* send header handler */
+
+ NULL, /* read POST data */
+ php_sapi_phpdbg_read_cookies, /* read Cookies */
+
+ php_sapi_phpdbg_register_vars, /* register server variables */
+ php_sapi_phpdbg_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+const opt_struct OPTIONS[] = { /* {{{ */
+ {'c', 1, "ini path override"},
+ {'d', 1, "define ini entry on command line"},
+ {'n', 0, "no php.ini"},
+ {'z', 1, "load zend_extension"},
+ /* phpdbg options */
+ {'q', 0, "no banner"},
+ {'v', 0, "disable quietness"},
+ {'s', 0, "enable stepping"},
+ {'b', 0, "boring colours"},
+ {'i', 1, "specify init"},
+ {'I', 0, "ignore init"},
+ {'O', 1, "opline log"},
+ {'r', 0, "run"},
+ {'E', 0, "step-through-eval"},
+ {'S', 1, "sapi-name"},
+ {'l', 1, "listen"},
+ {'a', 1, "address-or-any"},
+ {'x', 0, "xml output"},
+ {'V', 0, "version"},
+ {'-', 0, NULL}
+}; /* }}} */
+
+const char phpdbg_ini_hardcoded[] =
+"html_errors=Off\n"
+"register_argc_argv=On\n"
+"implicit_flush=On\n"
+"display_errors=Off\n"
+"log_errors=On\n"
+"max_execution_time=0\n"
+"max_input_time=-1\n"
+"error_log=\n\0";
+
+/* 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);
+
+void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
+{
+ zval tmp;
+ INI_DEFAULT("report_zend_debug", "0");
+} /* }}} */
+
+static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */
+{
+ /* print blurb */
+ if (!cleaning) {
+ phpdbg_xml("<intros>");
+ phpdbg_notice("intro", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
+ phpdbg_writeln("intro", "help=\"help\"", "To get help using phpdbg type \"help\" and press enter");
+ phpdbg_notice("intro", "report=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES);
+ phpdbg_xml("</intros>");
+ } else {
+ if (!(PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
+ phpdbg_notice(NULL, NULL, "Clean Execution Environment");
+ }
+
+ phpdbg_write("cleaninfo", "classes=\"%d\" functions=\"%d\" constants=\"%d\" includes=\"%d\"",
+ "Classes %d\n"
+ "Functions %d\n"
+ "Constants %d\n"
+ "Includes %d\n",
+ zend_hash_num_elements(EG(class_table)),
+ zend_hash_num_elements(EG(function_table)),
+ zend_hash_num_elements(EG(zend_constants)),
+ zend_hash_num_elements(&EG(included_files)));
+ }
+} /* }}} */
+
+static inline void phpdbg_sigint_handler(int signo) /* {{{ */
+{
+ TSRMLS_FETCH();
+
+ if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
+ /* we quit remote consoles on recv SIGINT */
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
+ zend_bailout();
+ }
+ } else {
+ /* set signalled only when not interactive */
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
+ if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
+ char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
+
+ phpdbg_set_sigsafe_mem(mem TSRMLS_CC);
+ zend_try {
+ phpdbg_force_interruption(TSRMLS_C);
+ } zend_end_try()
+ phpdbg_clear_sigsafe_mem(TSRMLS_C);
+ return;
+ }
+ PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
+ }
+ }
+} /* }}} */
+
+
+static void phpdbg_remote_close(int socket, FILE *stream) {
+ if (socket >= 0) {
+ phpdbg_close_socket(socket);
+ }
+
+ if (stream) {
+ fclose(stream);
+ }
+}
+
+/* don't inline this, want to debug it easily, will inline when done */
+static int phpdbg_remote_init(const char* address, unsigned short port, int server, int *socket, FILE **stream TSRMLS_DC) {
+ phpdbg_remote_close(*socket, *stream);
+
+ if (server < 0) {
+ phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port);
+
+ return FAILURE;
+ }
+
+ phpdbg_rlog(fileno(stderr), "accepting connections on %s:%u", address, port);
+ {
+ struct sockaddr_storage address;
+ socklen_t size = sizeof(address);
+ char buffer[20] = {0};
+ /* XXX error checks */
+ memset(&address, 0, size);
+ *socket = accept(server, (struct sockaddr *) &address, &size);
+ inet_ntop(AF_INET, &(((struct sockaddr_in *)&address)->sin_addr), buffer, sizeof(buffer));
+
+ phpdbg_rlog(fileno(stderr), "connection established from %s", buffer);
+ }
+
+#ifndef _WIN32
+ dup2(*socket, fileno(stdout));
+ dup2(*socket, fileno(stdin));
+
+ setbuf(stdout, NULL);
+
+ *stream = fdopen(*socket, "r+");
+
+ phpdbg_set_async_io(*socket);
+#endif
+ return SUCCESS;
+}
+
+#ifndef _WIN32
+/* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */
+void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */
+{
+ int flags;
+ size_t newlen;
+ size_t i/*, last_nl*/;
+ TSRMLS_FETCH();
+
+// if (!(info->si_band & POLLIN)) {
+// return; /* Not interested in writeablility etc., just interested in incoming data */
+// }
+
+ /* only non-blocking reading, avoid non-blocking writing */
+ flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0);
+ fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK);
+
+ do {
+ char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
+ size_t off = 0;
+
+ if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) {
+ break;
+ }
+ for (i = 0; i < newlen; i++) {
+ switch (mem[off + i]) {
+ case '\x03': /* ^C char */
+ if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
+ break; /* or quit ??? */
+ }
+ if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
+ phpdbg_set_sigsafe_mem(mem TSRMLS_CC);
+ zend_try {
+ phpdbg_force_interruption(TSRMLS_C);
+ } zend_end_try();
+ phpdbg_clear_sigsafe_mem(TSRMLS_C);
+ break;
+ }
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
+ }
+ break;
+/* case '\n':
+ zend_llist_add_element(PHPDBG_G(stdin), strndup()
+ last_nl = PHPDBG_G(stdin_buf).len + i;
+ break;
+*/ }
+ }
+ off += i;
+ } while (0);
+
+
+ fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags);
+} /* }}} */
+
+void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
+{
+ int is_handled = FAILURE;
+ TSRMLS_FETCH();
+
+ switch (sig) {
+ case SIGBUS:
+ case SIGSEGV:
+ if (PHPDBG_G(sigsegv_bailout)) {
+ LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
+ }
+ 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
+
+static inline zend_mm_heap *phpdbg_mm_get_heap() /* {{{ */
+{
+ zend_mm_heap *mm_heap;
+
+ TSRMLS_FETCH();
+
+ mm_heap = zend_mm_set_heap(NULL TSRMLS_CC);
+ zend_mm_set_heap(mm_heap TSRMLS_CC);
+
+ return mm_heap;
+} /* }}} */
+
+void *phpdbg_malloc_wrapper(size_t size) /* {{{ */
+{
+ return zend_mm_alloc(phpdbg_mm_get_heap(), size);
+} /* }}} */
+
+void phpdbg_free_wrapper(void *p) /* {{{ */
+{
+ zend_mm_free(phpdbg_mm_get_heap(), p);
+} /* }}} */
+
+void *phpdbg_realloc_wrapper(void *ptr, size_t size) /* {{{ */
+{
+ return zend_mm_realloc(phpdbg_mm_get_heap(), ptr, size);
+} /* }}} */
+
+int main(int argc, char **argv) /* {{{ */
+{
+ sapi_module_struct *phpdbg = &phpdbg_sapi_module;
+ char *sapi_name;
+ char *ini_entries;
+ int ini_entries_len;
+ char **zend_extensions = NULL;
+ zend_ulong zend_extensions_len = 0L;
+ zend_bool ini_ignore;
+ char *ini_override;
+ char *exec;
+ size_t exec_len;
+ char *init_file;
+ size_t init_file_len;
+ zend_bool init_file_default;
+ char *oplog_file;
+ size_t oplog_file_len;
+ zend_ulong flags;
+ char *php_optarg;
+ int php_optind, opt, show_banner = 1;
+ long cleaning = 0;
+ zend_bool remote = 0;
+ int run = 0;
+ int step = 0;
+
+#ifdef _WIN32
+ char *bp_tmp_file = NULL;
+#else
+ char bp_tmp_file[] = "/tmp/phpdbg.XXXXXX";
+#endif
+
+ char *address;
+ int listen = -1;
+ int server = -1;
+ int socket = -1;
+ FILE* stream = NULL;
+
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+
+#ifndef _WIN32
+ struct sigaction sigio_struct;
+ struct sigaction signal_struct;
+ signal_struct.sa_sigaction = phpdbg_signal_handler;
+ signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
+ sigio_struct.sa_sigaction = phpdbg_sigio_handler;
+ sigio_struct.sa_flags = SA_SIGINFO;
+#endif
+
+ address = strdup("127.0.0.1");
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /* sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+
+ tsrm_ls = ts_resource(0);
+#endif
+
+phpdbg_main:
+ if (!cleaning) {
+
+#ifdef _WIN32
+ bp_tmp_file = malloc(L_tmpnam);
+
+ if (bp_tmp_file) {
+ if (!tmpnam(bp_tmp_file)) {
+ free(bp_tmp_file);
+ bp_tmp_file = NULL;
+ }
+ }
+
+ if (!bp_tmp_file) {
+ phpdbg_error("tmpfile", "", "Unable to create temporary file");
+ return 1;
+ }
+#else
+ if (!mkstemp(bp_tmp_file)) {
+ memset(bp_tmp_file, 0, sizeof(bp_tmp_file));
+ }
+#endif
+
+ }
+ ini_entries = NULL;
+ ini_entries_len = 0;
+ ini_ignore = 0;
+ ini_override = NULL;
+ zend_extensions = NULL;
+ zend_extensions_len = 0L;
+ exec = NULL;
+ exec_len = 0;
+ init_file = NULL;
+ init_file_len = 0;
+ init_file_default = 1;
+ oplog_file = NULL;
+ oplog_file_len = 0;
+ flags = PHPDBG_DEFAULT_FLAGS;
+ php_optarg = NULL;
+ php_optind = 1;
+ opt = 0;
+ 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':
+ run++;
+ break;
+ case 'n':
+ ini_ignore = 1;
+ break;
+ case 'c':
+ if (ini_override) {
+ free(ini_override);
+ }
+ ini_override = strdup(php_optarg);
+ break;
+ case 'd': {
+ int len = strlen(php_optarg);
+ char *val;
+
+ if ((val = strchr(php_optarg, '='))) {
+ val++;
+ if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
+ ini_entries_len += (val - php_optarg);
+ memcpy(ini_entries + ini_entries_len, "\"", 1);
+ ini_entries_len++;
+ memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
+ ini_entries_len += len - (val - php_optarg);
+ memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
+ ini_entries_len += sizeof("\n\0\"") - 2;
+ } else {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
+ ini_entries_len += len + sizeof("\n\0") - 2;
+ }
+ } else {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
+ ini_entries_len += len + sizeof("=1\n\0") - 2;
+ }
+ } break;
+
+ case 'z':
+ zend_extensions_len++;
+ if (zend_extensions) {
+ zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
+ } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
+ zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
+ break;
+
+ /* begin phpdbg options */
+
+ case 'S': { /* set SAPI name */
+ if (sapi_name) {
+ free(sapi_name);
+ }
+ sapi_name = strdup(php_optarg);
+ } break;
+
+ case 'I': { /* ignore .phpdbginit */
+ init_file_default = 0;
+ } break;
+
+ case 'i': { /* set init file */
+ if (init_file) {
+ free(init_file);
+ }
+
+ init_file_len = strlen(php_optarg);
+ if (init_file_len) {
+ init_file = strdup(php_optarg);
+ }
+ } break;
+
+ case 'O': { /* set oplog output */
+ oplog_file_len = strlen(php_optarg);
+ if (oplog_file_len) {
+ oplog_file = strdup(php_optarg);
+ }
+ } break;
+
+ case 'v': /* set quietness off */
+ flags &= ~PHPDBG_IS_QUIET;
+ break;
+
+ case 's': /* set stepping on */
+ step = 1;
+ break;
+
+ case 'E': /* stepping through eval on */
+ flags |= PHPDBG_IS_STEPONEVAL;
+ break;
+
+ case 'b': /* set colours off */
+ flags &= ~PHPDBG_IS_COLOURED;
+ break;
+
+ case 'q': /* hide banner */
+ show_banner = 0;
+ break;
+
+ /* if you pass a listen port, we will read and write on listen port */
+ case 'l': /* set listen ports */
+ if (sscanf(php_optarg, "%d", &listen) != 1) {
+ listen = 8000;
+ }
+ break;
+
+ case 'a': { /* set bind address */
+ free(address);
+ if (!php_optarg) {
+ address = strdup("*");
+ } else address = strdup(php_optarg);
+ } break;
+
+ case 'x':
+ flags |= PHPDBG_WRITE_XML;
+ break;
+
+ case 'V': {
+ sapi_startup(phpdbg);
+ phpdbg->startup(phpdbg);
+ printf(
+ "phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2014 The PHP Group\n%s",
+ PHPDBG_VERSION,
+ __DATE__,
+ __TIME__,
+ PHP_VERSION,
+ get_zend_version()
+ );
+ sapi_deactivate(TSRMLS_C);
+ sapi_shutdown();
+ return 0;
+ } 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++;
+ }
+
+ if (sapi_name) {
+ phpdbg->name = sapi_name;
+ }
+
+ phpdbg->ini_defaults = phpdbg_ini_defaults;
+ phpdbg->phpinfo_as_text = 1;
+ phpdbg->php_ini_ignore_cwd = 1;
+
+ sapi_startup(phpdbg);
+
+ phpdbg->executable_location = argv[0];
+ phpdbg->phpinfo_as_text = 1;
+ phpdbg->php_ini_ignore = ini_ignore;
+ phpdbg->php_ini_path_override = ini_override;
+
+ if (ini_entries) {
+ ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded));
+ memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1);
+ memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2);
+ } else {
+ ini_entries = malloc(sizeof(phpdbg_ini_hardcoded));
+ 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));
+ ini_entries_len += (sizeof("zend_extension=")-1);
+ memcpy(&ini_entries[ini_entries_len], ze, ze_len);
+ ini_entries_len += ze_len;
+ memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
+
+ free(zend_extensions[zend_extension]);
+ zend_extension++;
+ }
+
+ free(zend_extensions);
+ }
+
+ phpdbg->ini_entries = ini_entries;
+
+ if (phpdbg->startup(phpdbg) == SUCCESS) {
+#ifdef _WIN32
+ EXCEPTION_POINTERS *xp;
+ __try {
+#endif
+ zend_mm_heap *mm_heap;
+
+ /* setup remote server if necessary */
+ if (!cleaning && listen > 0) {
+ server = phpdbg_open_socket(address, listen TSRMLS_CC);
+ if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream TSRMLS_CC) == FAILURE) {
+ exit(0);
+ }
+
+#ifndef _WIN32
+ sigaction(SIGIO, &sigio_struct, NULL);
+#endif
+
+ /* set remote flag to stop service shutting down upon quit */
+ remote = 1;
+ }
+
+ mm_heap = phpdbg_mm_get_heap();
+
+ if (mm_heap->use_zend_alloc) {
+ mm_heap->_malloc = phpdbg_malloc_wrapper;
+ mm_heap->_realloc = phpdbg_realloc_wrapper;
+ mm_heap->_free = phpdbg_free_wrapper;
+ mm_heap->use_zend_alloc = 0;
+ }
+
+ zend_activate(TSRMLS_C);
+
+ phpdbg_init_list(TSRMLS_C);
+
+ PHPDBG_G(original_free_function) = mm_heap->_free;
+ mm_heap->_free = phpdbg_watch_efree;
+
+ phpdbg_setup_watchpoints(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
+
+ PHPDBG_G(sapi_name_ptr) = sapi_name;
+
+ php_output_activate(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+
+ php_output_activate(TSRMLS_C);
+
+ 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);
+ }
+
+ /* do not install sigint handlers for remote consoles */
+ /* sending SIGINT then provides a decent way of shutting down the server */
+#ifndef _WIN32
+ if (listen < 0) {
+#endif
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
+ zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try();
+#else
+ signal(SIGINT, phpdbg_sigint_handler);
+#endif
+#ifndef _WIN32
+ }
+#endif
+
+ PG(modules_activated) = 0;
+
+ /* set flags from command line */
+ PHPDBG_G(flags) = flags;
+
+ /* setup io here */
+ if (remote) {
+ PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ }
+
+#ifndef _WIN32
+ PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
+ PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
+ PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
+ PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
+#else
+ /* XXX this is a complete mess here with FILE/fd/SOCKET,
+ we should let only one to survive probably. Need
+ a clean separation whether it's a remote or local
+ prompt. And what is supposed to go as user interaction,
+ error log, etc. */
+ if (remote) {
+ PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
+ PHPDBG_G(io)[PHPDBG_STDIN].fd = socket;
+ PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
+ PHPDBG_G(io)[PHPDBG_STDOUT].fd = socket;
+ } else {
+ PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
+ PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
+ PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
+ PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
+ }
+#endif
+ PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr;
+ PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
+
+#ifndef _WIN32
+ PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
+ php_stream_stdio_ops.write = phpdbg_stdiop_write;
+#endif
+
+ if (exec) { /* set execution context */
+ PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC);
+ PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec));
+
+ free(exec);
+ }
+
+ if (oplog_file) { /* open oplog */
+ PHPDBG_G(oplog) = fopen(oplog_file, "w+");
+ if (!PHPDBG_G(oplog)) {
+ phpdbg_error("oplog", "path=\"%s\"", "Failed to open oplog %s", oplog_file);
+ }
+ free(oplog_file);
+ }
+
+ /* set default colors */
+ phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC);
+ phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC);
+ phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green") TSRMLS_CC);
+
+ /* set default prompt */
+ phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT TSRMLS_CC);
+
+ /* Make stdin, stdout and stderr accessible from PHP scripts */
+ phpdbg_register_file_handles(TSRMLS_C);
+
+ if (show_banner) {
+ /* print blurb */
+ 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 {
+ phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC);
+ phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC);
+ } zend_end_try();
+ PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
+
+ /* quit if init says so */
+ if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
+ goto phpdbg_out;
+ }
+
+ /* step from here, not through init */
+ if (step) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
+ }
+
+ if (run) {
+ /* no need to try{}, run does it ... */
+ 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;
+ }
+ }
+
+phpdbg_interact:
+ /* phpdbg main() */
+ do {
+ zend_try {
+ phpdbg_interactive(1 TSRMLS_CC);
+ } zend_catch {
+ if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
+ FILE *bp_tmp_fp = fopen(bp_tmp_file, "w");
+ phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC);
+ fclose(bp_tmp_fp);
+ cleaning = 1;
+ } else {
+ cleaning = 0;
+ }
+
+ if (!cleaning) {
+ /* remote client disconnected */
+ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
+
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
+ /* renegociate connections */
+ phpdbg_remote_init(address, listen, server, &socket, &stream TSRMLS_CC);
+
+ /* set streams */
+ if (stream) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
+ }
+
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+ } else {
+ /* local consoles cannot disconnect, ignore EOF */
+ PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
+ }
+ }
+ }
+ } zend_end_try();
+ } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
+
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+
+ /* this is just helpful */
+ PG(report_memleaks) = 0;
+
+phpdbg_out:
+ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
+ goto phpdbg_interact;
+ }
+
+#ifdef _WIN32
+ } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
+ phpdbg_error("segfault", "", "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 _WIN32
+ /* reset it... else we risk a stack overflow upon next run (when clean'ing) */
+ php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
+#endif
+
+#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);
+ }
+
+ if (ini_override) {
+ free(ini_override);
+ }
+
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+
+ /* this is just helpful */
+ PG(report_memleaks) = 0;
+
+ php_request_shutdown((void*)0);
+
+ php_output_deactivate(TSRMLS_C);
+
+ zend_try {
+ php_module_shutdown(TSRMLS_C);
+ } zend_end_try();
+
+ sapi_shutdown();
+
+ }
+
+ if (cleaning || remote) {
+ goto phpdbg_main;
+ }
+
+#ifdef ZTS
+ /* bugggy */
+ /* tsrm_shutdown(); */
+#endif
+
+#ifndef _WIN32
+ if (address) {
+ free(address);
+ }
+#endif
+
+ if (PHPDBG_G(sapi_name_ptr)) {
+ free(PHPDBG_G(sapi_name_ptr));
+ }
+
+#ifdef _WIN32
+ free(bp_tmp_file);
+#else
+ unlink(bp_tmp_file);
+#endif
+
+ return 0;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h
new file mode 100644
index 0000000000..8c0dbb4afa
--- /dev/null
+++ b/sapi/phpdbg/phpdbg.h
@@ -0,0 +1,298 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_H
+#define PHPDBG_H
+
+#ifdef PHP_WIN32
+# define PHPDBG_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHPDBG_API __attribute__ ((visibility("default")))
+#else
+# define PHPDBG_API
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "php_getopt.h"
+#include "zend_builtin_functions.h"
+#include "zend_extensions.h"
+#include "zend_modules.h"
+#include "zend_globals.h"
+#include "zend_ini_scanner.h"
+#include "zend_stream.h"
+#ifndef _WIN32
+# include "zend_signal.h"
+#endif
+#include "SAPI.h"
+#include <fcntl.h>
+#include <sys/types.h>
+#if defined(_WIN32) && !defined(__MINGW32__)
+# include <windows.h>
+# include "config.w32.h"
+# include "win32/php_stdint.h"
+# undef strcasecmp
+# undef strncasecmp
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+#else
+# include "php_config.h"
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+#include "php_main.h"
+
+#ifdef ZTS
+# include "TSRM.h"
+#endif
+
+#ifdef HAVE_LIBREADLINE
+# include <readline/readline.h>
+# include <readline/history.h>
+#endif
+#ifdef HAVE_LIBEDIT
+# include <editline/readline.h>
+#endif
+
+/* {{{ remote console headers */
+#ifndef _WIN32
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <sys/select.h>
+# include <sys/types.h>
+# include <netdb.h>
+#endif /* }}} */
+
+/* {{{ strings */
+#define PHPDBG_NAME "phpdbg"
+#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.4.0"
+#define PHPDBG_INIT_FILENAME ".phpdbginit"
+#define PHPDBG_DEFAULT_PROMPT "prompt>"
+/* }}} */
+
+/* Hey, apple. One shouldn't define *functions* from the standard C library as marcos. */
+#ifdef memcpy
+#define memcpy_tmp(...) memcpy(__VA_ARGS__)
+#undef memcpy
+#define memcpy(...) memcpy_tmp(__VA_ARGS__)
+#endif
+
+#if !defined(PHPDBG_WEBDATA_TRANSFER_H) && !defined(PHPDBG_WEBHELPER_H)
+
+#ifdef ZTS
+# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
+#else
+# define PHPDBG_G(v) (phpdbg_globals.v)
+#endif
+
+#include "phpdbg_sigsafe.h"
+#include "phpdbg_out.h"
+#include "phpdbg_lexer.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_btree.h"
+#include "phpdbg_watch.h"
+#ifdef PHP_WIN32
+# include "phpdbg_sigio_win32.h"
+#endif
+
+int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
+
+#define PHPDBG_NEXT 2
+#define PHPDBG_UNTIL 3
+#define PHPDBG_FINISH 4
+#define PHPDBG_LEAVE 5
+
+/*
+ BEGIN: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
+*/
+
+/* {{{ tables */
+#define PHPDBG_BREAK_FILE 0
+#define PHPDBG_BREAK_SYM 1
+#define PHPDBG_BREAK_OPLINE 2
+#define PHPDBG_BREAK_METHOD 3
+#define PHPDBG_BREAK_COND 4
+#define PHPDBG_BREAK_OPCODE 5
+#define PHPDBG_BREAK_FUNCTION_OPLINE 6
+#define PHPDBG_BREAK_METHOD_OPLINE 7
+#define PHPDBG_BREAK_FILE_OPLINE 8
+#define PHPDBG_BREAK_MAP 9
+#define PHPDBG_BREAK_TABLES 10 /* }}} */
+
+/* {{{ flags */
+#define PHPDBG_HAS_FILE_BP (1<<1)
+#define PHPDBG_HAS_SYM_BP (1<<2)
+#define PHPDBG_HAS_OPLINE_BP (1<<3)
+#define PHPDBG_HAS_METHOD_BP (1<<4)
+#define PHPDBG_HAS_COND_BP (1<<5)
+#define PHPDBG_HAS_OPCODE_BP (1<<6)
+#define PHPDBG_HAS_FUNCTION_OPLINE_BP (1<<7)
+#define PHPDBG_HAS_METHOD_OPLINE_BP (1<<8)
+#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9) /* }}} */
+
+/*
+ END: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
+*/
+
+#define PHPDBG_IN_COND_BP (1<<10)
+#define PHPDBG_IN_EVAL (1<<11)
+
+#define PHPDBG_IS_STEPPING (1<<12)
+#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_WRITE_XML (1<<29)
+
+#define PHPDBG_SHOW_REFCOUNTS (1<<30)
+
+#define PHPDBG_IN_SIGNAL_HANDLER (1<<30)
+
+#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE)
+#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
+#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
+
+#ifndef _WIN32
+# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED)
+#else
+# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_BP_ENABLED)
+#endif /* }}} */
+
+/* {{{ output descriptors */
+#define PHPDBG_STDIN 0
+#define PHPDBG_STDOUT 1
+#define PHPDBG_STDERR 2
+#define PHPDBG_IO_FDS 3 /* }}} */
+
+#define phpdbg_try_access \
+ { \
+ JMP_BUF *__orig_bailout = PHPDBG_G(sigsegv_bailout); \
+ JMP_BUF __bailout; \
+ \
+ PHPDBG_G(sigsegv_bailout) = &__bailout; \
+ if (SETJMP(__bailout) == 0) {
+#define phpdbg_catch_access \
+ } else { \
+ PHPDBG_G(sigsegv_bailout) = __orig_bailout;
+#define phpdbg_end_try_access() \
+ } \
+ PHPDBG_G(sigsegv_bailout) = __orig_bailout; \
+ }
+
+
+/* {{{ 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 */
+
+ phpdbg_lexer_data lexer; /* lexer data */
+ phpdbg_param_t *parser_stack; /* param stack during lexer / parser phase */
+
+#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 */
+ zend_op_array *ops; /* op_array */
+ zval *retval; /* return value */
+ int bp_count; /* breakpoint count */
+ int vmret; /* return from last opcode handler execution */
+
+ zend_op_array *(*compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
+ HashTable file_sources;
+
+ FILE *oplog; /* opline log */
+ struct {
+ FILE *ptr;
+ int fd;
+ } io[PHPDBG_IO_FDS]; /* io */
+ size_t (*php_stdiop_write)(php_stream *, const char *, size_t TSRMLS_DC);
+ int in_script_xml; /* in <stream> output mode */
+ struct {
+ zend_bool active;
+ int type;
+ int fd;
+ char *tag;
+ char *msg;
+ int msglen;
+ char *xml;
+ int xmllen;
+ } err_buf; /* error buffer */
+ zend_ulong req_id; /* "request id" to keep track of commands */
+
+ char *prompt[2]; /* prompt */
+ const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */
+ char *buffer; /* buffer */
+ zend_bool last_was_newline; /* check if we don't need to output a newline upon next phpdbg_error or phpdbg_notice */
+
+ char input_buffer[PHPDBG_MAX_CMD]; /* stdin input buffer */
+ int input_buflen; /* length of stdin input buffer */
+ phpdbg_signal_safe_mem sigsafe_mem; /* memory to use in async safe environment (only once!) */
+
+ JMP_BUF *sigsegv_bailout; /* bailout address for accesibility probing */
+
+ zend_ulong flags; /* phpdbg flags */
+
+ char *socket_path; /* phpdbg.path ini setting */
+ char *sapi_name_ptr; /* store sapi name to free it if necessary to not leak memory */
+ int socket_fd; /* file descriptor to socket (wait command) (-1 if unused) */
+ int socket_server_fd; /* file descriptor to master socket (wait command) (-1 if unused) */
+#ifdef PHP_WIN32
+ HANDLE sigio_watcher_thread; /* sigio watcher thread handle */
+ struct win32_sigio_watcher_data swd;
+#endif
+ int8_t eol;
+ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
+
+#endif
+
+#endif /* PHPDBG_H */
diff --git a/sapi/phpdbg/phpdbg.init.d b/sapi/phpdbg/phpdbg.init.d
new file mode 100755
index 0000000000..99a1ab328b
--- /dev/null
+++ b/sapi/phpdbg/phpdbg.init.d
@@ -0,0 +1,122 @@
+################################################################
+# File: /etc/init.d/phpdbg #
+# Author: krakjoe #
+# Purpose: Daemonize phpdbg automatically on boot #
+# chkconfig: 2345 07 09 #
+# description: Starts, stops and restarts phpdbg daemon #
+################################################################
+LOCKFILE=/var/lock/subsys/phpdbg
+PIDFILE=/var/run/phpdbg.pid
+STDIN=4000
+STDOUT=8000
+################################################################
+# Either set path to phpdbg here or rely on phpdbg in ENV/PATH #
+################################################################
+if [ "x${PHPDBG}" == "x" ]; then
+ PHPDBG=$(which phpdbg 2>/dev/null)
+fi
+################################################################
+# Options to pass to phpdbg upon boot #
+################################################################
+OPTIONS=
+LOGFILE=/var/log/phpdbg.log
+################################################################
+# STOP EDITING STOP EDITING STOP EDITING STOP EDITING #
+################################################################
+. /etc/rc.d/init.d/functions
+RETVAL=1
+################################################################
+insanity()
+{
+ if [ "x${PHPDBG}" == "x" ]; then
+ PHPDBG=$(which phpdbg 2>>/dev/null)
+ if [ $? != 0 ]; then
+ echo -n $"Fatal: cannot find phpdbg ${PHPDBG}"
+ echo_failure
+ echo
+ return 1
+ fi
+ else
+ if [ ! -x ${PHPDBG} ]; then
+ echo -n $"Fatal: cannot execute phpdbg ${PHPDBG}"
+ echo_failure
+ echo
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+start()
+{
+ insanity
+
+ if [ $? -eq 1 ]; then
+ return $RETVAL
+ fi
+
+ echo -n $"Starting: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
+ nohup ${PHPDBG} -l${STDIN}/${STDOUT} ${OPTIONS} 2>>${LOGFILE} 1>/dev/null </dev/null &
+ PID=$!
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]; then
+ echo $PID > $PIDFILE
+ echo_success
+ else
+ echo_failure
+ fi
+ echo
+ [ $RETVAL = 0 ] && touch ${LOCKFILE}
+ return $RETVAL
+}
+
+stop()
+{
+ insanity
+
+ if [ $? -eq 1 ]; then
+ return $RETVAL
+ fi
+
+ if [ -f ${LOCKFILE} ] && [ -f ${PIDFILE} ]
+ then
+ echo -n $"Stopping: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
+ kill -s TERM $(cat $PIDFILE)
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]; then
+ echo_success
+ else
+ echo_failure
+ fi
+ echo
+ [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE}
+ else
+ echo -n $"Error: phpdbg not running"
+ echo_failure
+ echo
+ [ $RETVAL = 1 ]
+ fi
+ return $RETVAL
+}
+##################################################################
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status $PHPDBG
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "usage: $0 start|stop|restart|status"
+ ;;
+esac
+###################################################################
+exit $RETVAL
diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c
new file mode 100644
index 0000000000..81674a0daf
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_bp.c
@@ -0,0 +1,1623 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "zend_hash.h"
+#include "phpdbg.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_opcode.h"
+#include "zend_globals.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+/* {{{ private api functions */
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array* TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function* TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array* TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar TSRMLS_DC);
+static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data TSRMLS_DC); /* }}} */
+
+/*
+* Note:
+* A break point must always set the correct id and type
+* A set breakpoint function must always map new points
+*/
+static inline void _phpdbg_break_mapping(int id, HashTable *table TSRMLS_DC)
+{
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (id), (void**) &table, sizeof(void*), NULL);
+}
+
+#define PHPDBG_BREAK_MAPPING(id, table) _phpdbg_break_mapping(id, table TSRMLS_CC)
+#define PHPDBG_BREAK_UNMAPPING(id) \
+ zend_hash_index_del(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (id))
+
+#define PHPDBG_BREAK_INIT(b, t) do {\
+ b.id = PHPDBG_G(bp_count)++; \
+ b.type = t; \
+ b.disabled = 0;\
+ b.hits = 0; \
+} while(0)
+
+static void phpdbg_file_breaks_dtor(void *data) /* {{{ */
+{
+ phpdbg_breakfile_t *bp = (phpdbg_breakfile_t*) data;
+
+ efree((char*)bp->filename);
+} /* }}} */
+
+static void phpdbg_class_breaks_dtor(void *data) /* {{{ */
+{
+ phpdbg_breakmethod_t *bp = (phpdbg_breakmethod_t*) data;
+
+ efree((char*)bp->class_name);
+ efree((char*)bp->func_name);
+} /* }}} */
+
+static void phpdbg_opline_class_breaks_dtor(void *data) /* {{{ */
+{
+ zend_hash_destroy((HashTable *)data);
+} /* }}} */
+
+static void phpdbg_opline_breaks_dtor(void *data) /* {{{ */
+{
+ phpdbg_breakopline_t *bp = (phpdbg_breakopline_t *) data;
+
+ if (bp->class_name) {
+ efree((char*)bp->class_name);
+ }
+ if (bp->func_name) {
+ efree((char*)bp->func_name);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_reset_breakpoints(TSRMLS_D) /* {{{ */
+{
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP])) {
+ HashPosition position[2];
+ HashTable **table = NULL;
+
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (void **) &table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0])) {
+ phpdbg_breakbase_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex((*table), &position[1]);
+ zend_hash_get_current_data_ex((*table), (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex((*table), &position[1])) {
+ brake->hits = 0;
+ }
+ }
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
+{
+ HashPosition position[2];
+ HashTable **table = NULL;
+ zend_ulong id = 0L;
+
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP])) {
+ phpdbg_notice("exportbreakpoint", "count=\"%d\"", "Exporting %d breakpoints", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]));
+ /* this only looks like magic, it isn't */
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (void **) &table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], &position[0])) {
+ phpdbg_breakbase_t *brake;
+
+ zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], NULL, NULL, &id, 0, &position[0]);
+
+ for (zend_hash_internal_pointer_reset_ex((*table), &position[1]);
+ zend_hash_get_current_data_ex((*table), (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex((*table), &position[1])) {
+ if (brake->id == id) {
+ switch (brake->type) {
+ case PHPDBG_BREAK_FILE: {
+ fprintf(handle,
+ "break %s:%lu\n",
+ ((phpdbg_breakfile_t*)brake)->filename,
+ ((phpdbg_breakfile_t*)brake)->line);
+ } break;
+
+ case PHPDBG_BREAK_SYM: {
+ fprintf(handle,
+ "break %s\n",
+ ((phpdbg_breaksymbol_t*)brake)->symbol);
+ } break;
+
+ case PHPDBG_BREAK_METHOD: {
+ fprintf(handle,
+ "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 %s::%s#%ld\n",
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num);
+ } break;
+
+ case PHPDBG_BREAK_FUNCTION_OPLINE: {
+ fprintf(handle,
+ "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 %s:#%ld\n",
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num);
+ } break;
+
+ case PHPDBG_BREAK_OPCODE: {
+ fprintf(handle,
+ "break %s\n",
+ ((phpdbg_breakop_t*)brake)->name);
+ } break;
+
+ case PHPDBG_BREAK_COND: {
+ phpdbg_breakcond_t *conditional = (phpdbg_breakcond_t*) brake;
+
+ if (conditional->paramed) {
+ switch (conditional->param.type) {
+ case STR_PARAM:
+ fprintf(handle,
+ "break at %s if %s\n", conditional->param.str, conditional->code);
+ break;
+
+ case METHOD_PARAM:
+ fprintf(handle,
+ "break at %s::%s if %s\n",
+ conditional->param.method.class, conditional->param.method.name,
+ conditional->code);
+ break;
+
+ case FILE_PARAM:
+ fprintf(handle,
+ "break at %s:%lu if %s\n",
+ conditional->param.file.name, conditional->param.file.line,
+ conditional->code);
+ break;
+
+ default: { /* do nothing */ } break;
+ }
+ } else {
+ fprintf(
+ handle, "break if %s\n", conditional->code);
+ }
+ } break;
+ }
+ }
+ }
+ }
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */
+{
+ 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 = 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;
+
+ zend_hash_init(&breaks, 8, NULL, phpdbg_file_breaks_dtor, 0);
+
+ zend_hash_update(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],
+ path, path_len, &breaks, sizeof(HashTable),
+ (void**)&broken);
+ }
+
+ if (!zend_hash_index_exists(broken, line_num)) {
+ PHPDBG_G(flags) |= PHPDBG_HAS_FILE_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FILE);
+ new_break.filename = estrndup(path, path_len);
+ new_break.line = line_num;
+
+ zend_hash_index_update( broken, line_num, (void**)&new_break, sizeof(phpdbg_breakfile_t), NULL);
+
+ phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" file=\"%s\" line=\"%ld\"", "Breakpoint #%d added at %s:%ld",
+ new_break.id, new_break.filename, new_break.line);
+
+ PHPDBG_BREAK_MAPPING(new_break.id, broken);
+ } else {
+ phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" file=\"%s\" line=\"%ld\"", "Breakpoint at %s:%ld exists", path, line_num);
+ }
+
+ } else {
+ phpdbg_error("breakpoint", "type=\"notregular\" add=\"fail\" file=\"%s\"", "Cannot set breakpoint in %s, it is not a regular file", path);
+ }
+ } else {
+ phpdbg_error("breakpoint", "type=\"nofile\" add=\"fail\" file=\"%s\"", "Cannot stat %s, it does not exist", path);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_symbol(const char *name, size_t name_len TSRMLS_DC) /* {{{ */
+{
+ if (!zend_hash_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], name, name_len)) {
+ phpdbg_breaksymbol_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_SYM_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_SYM);
+ new_break.symbol = estrndup(name, name_len);
+
+ zend_hash_update(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], new_break.symbol,
+ name_len, &new_break, sizeof(phpdbg_breaksymbol_t), NULL);
+
+ phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" function=\"%s\"", "Breakpoint #%d added at %s",
+ new_break.id, new_break.symbol);
+
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ } else {
+ phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" function=\"%s\"", "Breakpoint exists at %s", name);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char *func_name TSRMLS_DC) /* {{{ */
+{
+ HashTable class_breaks, *class_table;
+ size_t class_len = strlen(class_name);
+ size_t func_len = strlen(func_name);
+ char *lcname = zend_str_tolower_dup(func_name, func_len);
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_name,
+ class_len, (void**)&class_table) != SUCCESS) {
+ zend_hash_init(&class_breaks, 8, NULL, phpdbg_class_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD],
+ class_name, class_len,
+ (void**)&class_breaks, sizeof(HashTable), (void**)&class_table);
+ }
+
+ if (!zend_hash_exists(class_table, lcname, func_len)) {
+ phpdbg_breakmethod_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_METHOD);
+ new_break.class_name = estrndup(class_name, class_len);
+ new_break.class_len = class_len;
+ new_break.func_name = estrndup(func_name, func_len);
+ new_break.func_len = func_len;
+
+ zend_hash_update(class_table, lcname, func_len,
+ &new_break, sizeof(phpdbg_breakmethod_t), NULL);
+
+ phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" method=\"%s::%s\"", "Breakpoint #%d added at %s::%s",
+ new_break.id, class_name, func_name);
+
+ PHPDBG_BREAK_MAPPING(new_break.id, class_table);
+ } else {
+ phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" method=\"%s::%s\"", "Breakpoint exists at %s::%s", class_name, func_name);
+ }
+
+ efree(lcname);
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline)) {
+ phpdbg_breakline_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPLINE);
+ new_break.name = NULL;
+ new_break.opline = opline;
+ new_break.base = NULL;
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline,
+ &new_break, sizeof(phpdbg_breakline_t), NULL);
+
+ phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" opline=\"%#lx\"", "Breakpoint #%d added at %#lx",
+ new_break.id, new_break.opline);
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ } else {
+ phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" opline=\"%#lx\"", "Breakpoint exists at %#lx", opline);
+ }
+} /* }}} */
+
+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 (brake->class_name == NULL) {
+ phpdbg_error("breakpoint", "type=\"maxoplines\" add=\"fail\" maxoplinenum=\"%d\" function=\"%s\" usedoplinenum=\"%ld\"", "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) {
+ phpdbg_error("breakpoint", "type=\"maxoplines\" add=\"fail\" maxoplinenum=\"%d\" file=\"%s\" usedoplinenum=\"%ld\"", "There are only %d oplines in file %s (breaking at opline %ld impossible)", op_array->last, brake->class_name, brake->opline_num);
+ } else {
+ phpdbg_error("breakpoint", "type=\"maxoplines\" add=\"fail\" maxoplinenum=\"%d\" method=\"%s::%s\" usedoplinenum=\"%ld\"", "There are only %d oplines in method %s::%s (breaking at opline %ld impossible)", op_array->last, brake->class_name, brake->func_name, brake->opline_num);
+ }
+
+ return FAILURE;
+ }
+
+ opline_break.disabled = 0;
+ opline_break.hits = 0;
+ opline_break.id = brake->id;
+ opline_break.opline = brake->opline = (zend_ulong)(op_array->opcodes + brake->opline_num);
+ opline_break.name = NULL;
+ opline_break.base = brake;
+ if (op_array->scope) {
+ opline_break.type = PHPDBG_BREAK_METHOD_OPLINE;
+ } else if (op_array->function_name) {
+ opline_break.type = PHPDBG_BREAK_FUNCTION_OPLINE;
+ } else {
+ opline_break.type = PHPDBG_BREAK_FILE_OPLINE;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline_break.opline, &opline_break, sizeof(phpdbg_breakline_t), NULL);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+ HashTable *func_table = &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE];
+ HashTable *oplines_table;
+ HashPosition position;
+ phpdbg_breakopline_t *brake;
+
+ if (op_array->scope != NULL &&
+ zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], op_array->scope->name, op_array->scope->name_length, (void **)&func_table) == FAILURE) {
+ return;
+ }
+
+ if (op_array->function_name == NULL) {
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], op_array->filename, strlen(op_array->filename), (void **)&oplines_table) == FAILURE) {
+ return;
+ }
+ } else if (zend_hash_find(func_table, op_array->function_name?op_array->function_name:"", op_array->function_name?strlen(op_array->function_name):0, (void **)&oplines_table) == FAILURE) {
+ return;
+ }
+
+ for (zend_hash_internal_pointer_reset_ex(oplines_table, &position);
+ zend_hash_get_current_data_ex(oplines_table, (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(oplines_table, &position)) {
+ if (phpdbg_resolve_op_array_break(brake, op_array TSRMLS_CC) == SUCCESS) {
+ phpdbg_breakline_t *opline_break;
+
+ zend_hash_internal_pointer_end(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_get_current_data(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (void **)&opline_break);
+
+ phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" symbol=\"%s\" num=\"%ld\" opline=\"%#lx\"", "Breakpoint #%d resolved at %s%s%s#%ld (opline %#lx)",
+ brake->id,
+ brake->class_name?brake->class_name:"",
+ brake->class_name&&brake->func_name?"::":"",
+ brake->func_name?brake->func_name:"",
+ brake->opline_num,
+ brake->opline);
+ }
+ }
+} /* }}} */
+
+PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC) /* {{{ */
+{
+ HashTable *func_table = EG(function_table);
+ zend_function *func;
+
+ if (new_break->func_name == NULL) {
+ if (EG(current_execute_data) == NULL) {
+ if (PHPDBG_G(ops) != NULL && !memcmp(PHPDBG_G(ops)->filename, new_break->class_name, new_break->class_len)) {
+ if (phpdbg_resolve_op_array_break(new_break, PHPDBG_G(ops) TSRMLS_CC) == SUCCESS) {
+ return SUCCESS;
+ } else {
+ return 2;
+ }
+ }
+ return FAILURE;
+ } else {
+ zend_execute_data *execute_data = EG(current_execute_data);
+ do {
+ if (execute_data->op_array->function_name == NULL && execute_data->op_array->scope == NULL && !memcmp(execute_data->op_array->filename, new_break->class_name, new_break->class_len)) {
+ if (phpdbg_resolve_op_array_break(new_break, execute_data->op_array TSRMLS_CC) == SUCCESS) {
+ return SUCCESS;
+ } else {
+ return 2;
+ }
+ }
+ } while ((execute_data = execute_data->prev_execute_data) != NULL);
+ return FAILURE;
+ }
+ }
+
+ if (new_break->class_name != NULL) {
+ zend_class_entry **ce;
+ if (zend_hash_find(EG(class_table), zend_str_tolower_dup(new_break->class_name, new_break->class_len), new_break->class_len + 1, (void **)&ce) == FAILURE) {
+ return FAILURE;
+ }
+ func_table = &(*ce)->function_table;
+ }
+
+ if (zend_hash_find(func_table, zend_str_tolower_dup(new_break->func_name, new_break->func_len), new_break->func_len + 1, (void **)&func) == FAILURE) {
+ if (new_break->class_name != NULL && new_break->func_name != NULL) {
+ phpdbg_error("breakpoint", "type=\"nomethod\" method=\"%s::%s\"", "Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name);
+ return 2;
+ }
+ return FAILURE;
+ }
+
+ if (func->type != ZEND_USER_FUNCTION) {
+ if (new_break->class_name == NULL) {
+ phpdbg_error("breakpoint", "type=\"internalfunction\" function=\"%s\"", "%s is not an user defined function, no oplines exist", new_break->func_name);
+ } else {
+ phpdbg_error("breakpoint", "type=\"internalfunction\" method=\"%s::%s\"", "%s::%s is not an user defined method, no oplines exist", new_break->class_name, new_break->func_name);
+ }
+ return 2;
+ }
+
+ if (phpdbg_resolve_op_array_break(new_break, &func->op_array TSRMLS_CC) == FAILURE) {
+ return 2;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakopline_t new_break;
+ HashTable class_breaks, *class_table;
+ HashTable method_breaks, *method_table;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_METHOD_OPLINE);
+ new_break.func_len = strlen(method);
+ new_break.func_name = estrndup(method, new_break.func_len);
+ new_break.class_len = strlen(class);
+ new_break.class_name = estrndup(class, new_break.class_len);
+ new_break.opline_num = opline;
+ new_break.opline = 0;
+
+ switch (phpdbg_resolve_opline_break(&new_break TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_notice("breakpoint", "pending=\"pending\" id=\"%d\" method=\"%::%s\" num=\"%ld\"", "Pending breakpoint #%d at %s::%s#%ld", new_break.id, new_break.class_name, new_break.func_name, opline);
+ break;
+
+ case SUCCESS:
+ phpdbg_notice("breakpoint", "id=\"%d\" method=\"%::%s\" num=\"%ld\"", "Breakpoint #%d added at %s::%s#%ld", new_break.id, new_break.class_name, new_break.func_name, opline);
+ break;
+
+ case 2:
+ return;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], new_break.class_name, new_break.class_len, (void **)&class_table) == FAILURE) {
+ zend_hash_init(&class_breaks, 8, NULL, phpdbg_opline_class_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE],
+ new_break.class_name,
+ new_break.class_len,
+ (void **)&class_breaks, sizeof(HashTable), (void **)&class_table);
+ }
+
+ if (zend_hash_find(class_table, new_break.func_name, new_break.func_len, (void **)&method_table) == FAILURE) {
+ zend_hash_init(&method_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
+ zend_hash_update(
+ class_table,
+ new_break.func_name,
+ new_break.func_len,
+ (void **)&method_breaks, sizeof(HashTable), (void **)&method_table);
+ }
+
+ if (zend_hash_index_exists(method_table, opline)) {
+ phpdbg_error("breakpoint", "type=\"exists\" method=\"%s\" num=\"%ld\"", "Breakpoint already exists for %s::%s#%ld", new_break.class_name, new_break.func_name, opline);
+ efree((char*)new_break.func_name);
+ efree((char*)new_break.class_name);
+ PHPDBG_G(bp_count)--;
+ return;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_OPLINE_BP;
+
+ PHPDBG_BREAK_MAPPING(new_break.id, method_table);
+
+ zend_hash_index_update(method_table, opline, &new_break, sizeof(phpdbg_breakopline_t), NULL);
+}
+
+PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakopline_t new_break;
+ HashTable func_breaks, *func_table;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FUNCTION_OPLINE);
+ new_break.func_len = strlen(function);
+ new_break.func_name = estrndup(function, new_break.func_len);
+ new_break.class_len = 0;
+ new_break.class_name = NULL;
+ new_break.opline_num = opline;
+ new_break.opline = 0;
+
+ switch (phpdbg_resolve_opline_break(&new_break TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_notice("breakpoint", "pending=\"pending\" id=\"%d\" function=\"%s\" num=\"%ld\"", "Pending breakpoint #%d at %s#%ld", new_break.id, new_break.func_name, opline);
+ break;
+
+ case SUCCESS:
+ phpdbg_notice("breakpoint", "id=\"%d\" function=\"%s\" num=\"%ld\"", "Breakpoint #%d added at %s#%ld", new_break.id, new_break.func_name, opline);
+ break;
+
+ case 2:
+ return;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], new_break.func_name, new_break.func_len, (void **)&func_table) == FAILURE) {
+ zend_hash_init(&func_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE],
+ new_break.func_name,
+ new_break.func_len,
+ (void **)&func_breaks, sizeof(HashTable), (void **)&func_table);
+ }
+
+ if (zend_hash_index_exists(func_table, opline)) {
+ phpdbg_error("breakpoint", "type=\"exists\" function=\"%s\" num=\"%ld\"", "Breakpoint already exists for %s#%ld", new_break.func_name, opline);
+ efree((char*)new_break.func_name);
+ PHPDBG_G(bp_count)--;
+ return;
+ }
+
+ PHPDBG_BREAK_MAPPING(new_break.id, func_table);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_FUNCTION_OPLINE_BP;
+
+ zend_hash_index_update(func_table, opline, &new_break, sizeof(phpdbg_breakopline_t), NULL);
+}
+
+PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakopline_t new_break;
+ HashTable file_breaks, *file_table;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FILE_OPLINE);
+ new_break.func_len = 0;
+ new_break.func_name = NULL;
+ new_break.class_len = strlen(file);
+ new_break.class_name = estrndup(file, new_break.class_len);
+ new_break.opline_num = opline;
+ new_break.opline = 0;
+
+ switch (phpdbg_resolve_opline_break(&new_break TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_notice("breakpoint", "pending=\"pending\" id=\"%d\" file=\"%s\" num=\"%ld\"", "Pending breakpoint #%d at %s:%ld", new_break.id, new_break.class_name, opline);
+ break;
+
+ case SUCCESS:
+ phpdbg_notice("breakpoint", "id=\"%d\" file=\"%s\" num=\"%ld\"", "Breakpoint #%d added at %s:%ld", new_break.id, new_break.class_name, opline);
+ break;
+
+ case 2:
+ return;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], new_break.class_name, new_break.class_len, (void **)&file_table) == FAILURE) {
+ zend_hash_init(&file_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
+ zend_hash_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE],
+ new_break.class_name,
+ new_break.class_len,
+ (void **)&file_breaks, sizeof(HashTable), (void **)&file_table);
+ }
+
+ if (zend_hash_index_exists(file_table, opline)) {
+ phpdbg_error("breakpoint", "type=\"exists\" file=\"%s\" num=\"%d\"", "Breakpoint already exists for %s:%ld", new_break.class_name, opline);
+ efree((char*)new_break.class_name);
+ PHPDBG_G(bp_count)--;
+ return;
+ }
+
+ PHPDBG_BREAK_MAPPING(new_break.id, file_table);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_FILE_OPLINE_BP;
+
+ zend_hash_index_update(file_table, opline, &new_break, sizeof(phpdbg_breakopline_t), NULL);
+}
+
+PHPDBG_API void phpdbg_set_breakpoint_opcode(const char *name, size_t name_len TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakop_t new_break;
+ zend_ulong hash = zend_hash_func(name, name_len);
+
+ if (zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], hash)) {
+ phpdbg_error("breakpoint", "type=\"exists\" opcode=\"%s\"", "Breakpoint exists for %s", name);
+ return;
+ }
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPCODE);
+ new_break.hash = hash;
+ new_break.name = estrndup(name, name_len);
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], hash,
+ &new_break, sizeof(phpdbg_breakop_t), NULL);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPCODE_BP;
+
+ phpdbg_notice("breakpoint", "id=\"%d\" opcode=\"%s\"", "Breakpoint #%d added at %s", new_break.id, name);
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ */
+{
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (zend_ulong) opline)) {
+ phpdbg_breakline_t new_break;
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPLINE);
+ new_break.opline = (zend_ulong) opline;
+
+ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE],
+ (zend_ulong) opline, &new_break, sizeof(phpdbg_breakline_t), NULL);
+
+ phpdbg_notice("breakpoint", "id=\"%d\" opline=\"%#lx\"", "Breakpoint #%d added at %#lx", new_break.id, new_break.opline);
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ } else {
+ phpdbg_error("breakpoint", "type=\"exists\" opline=\"%#lx\"", "Breakpoint exists for opline %#lx", (zend_ulong) opline);
+ }
+} /* }}} */
+
+static inline void phpdbg_create_conditional_break(phpdbg_breakcond_t *brake, const phpdbg_param_t *param, const char *expr, size_t expr_len, zend_ulong hash TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakcond_t new_break;
+ zend_uint cops = CG(compiler_options);
+ zval pv;
+
+ PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_COND);
+ new_break.hash = hash;
+
+ if (param) {
+ new_break.paramed = 1;
+ phpdbg_copy_param(
+ param, &new_break.param TSRMLS_CC);
+ } else {
+ new_break.paramed = 0;
+ }
+
+ cops = CG(compiler_options);
+
+ CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
+
+ new_break.code = estrndup(expr, expr_len);
+ new_break.code_len = expr_len;
+
+ Z_STRLEN(pv) = expr_len + sizeof("return ;") - 1;
+ Z_STRVAL(pv) = emalloc(Z_STRLEN(pv) + 1);
+ memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1);
+ memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len);
+ Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';';
+ Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0';
+ Z_TYPE(pv) = IS_STRING;
+
+ new_break.ops = zend_compile_string(&pv, "Conditional Breakpoint Code" TSRMLS_CC);
+
+ zval_dtor(&pv);
+
+ if (new_break.ops) {
+ zend_hash_index_update(
+ &PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break,
+ sizeof(phpdbg_breakcond_t), (void**)&brake);
+
+ phpdbg_notice("breakpoint", "id=\"%d\" expression=\"%s\" ptr=\"%p\"", "Conditional breakpoint #%d added %s/%p", brake->id, brake->code, brake->ops);
+
+ PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP;
+ PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+ } else {
+ phpdbg_error("compile", "expression=\"%s\"", "Failed to compile code for expression %s", expr);
+ efree((char*)new_break.code);
+ PHPDBG_G(bp_count)--;
+ }
+ CG(compiler_options) = cops;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_len TSRMLS_DC) /* {{{ */
+{
+ zend_ulong expr_hash = zend_inline_hash_func(expr, expr_len);
+ phpdbg_breakcond_t new_break;
+
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+ phpdbg_create_conditional_break(
+ &new_break, NULL, expr, expr_len, expr_hash TSRMLS_CC);
+ } else {
+ phpdbg_error("breakpoint", "type=\"exists\" expression=\"%s\"", "Conditional break %s exists", expr);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ 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, param, condition->str, condition->len, hash TSRMLS_CC);
+ } else {
+ phpdbg_notice("breakpoint", "type=\"exists\" arg=\"%s\"", "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) /* {{{ */
+{
+ HashTable *breaks;
+ phpdbg_breakbase_t *brake;
+ size_t name_len = strlen(op_array->filename);
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], op_array->filename,
+ name_len, (void**)&breaks) == FAILURE) {
+ return NULL;
+ }
+
+ if (zend_hash_index_find(breaks, (*EG(opline_ptr))->lineno, (void**)&brake) == SUCCESS) {
+ return brake;
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function *fbc TSRMLS_DC) /* {{{ */
+{
+ const char *fname;
+ zend_op_array *ops;
+ phpdbg_breakbase_t *brake;
+
+ if (fbc->type != ZEND_USER_FUNCTION) {
+ return NULL;
+ }
+
+ ops = (zend_op_array*)fbc;
+
+ if (ops->scope) {
+ /* find method breaks here */
+ return phpdbg_find_breakpoint_method(ops TSRMLS_CC);
+ }
+
+ fname = ops->function_name;
+
+ if (!fname) {
+ fname = "main";
+ }
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], fname, strlen(fname), (void**)&brake) == SUCCESS) {
+ return brake;
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array *ops TSRMLS_DC) /* {{{ */
+{
+ HashTable *class_table;
+ phpdbg_breakbase_t *brake;
+
+ if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], ops->scope->name,
+ ops->scope->name_length, (void**)&class_table) == SUCCESS) {
+ char *lcname = zend_str_tolower_dup(ops->function_name, strlen(ops->function_name));
+ size_t lcname_len = strlen(lcname);
+
+ if (zend_hash_find(
+ class_table,
+ lcname,
+ lcname_len, (void**)&brake) == SUCCESS) {
+ efree(lcname);
+ return brake;
+ }
+
+ efree(lcname);
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakline_t *brake;
+
+ if (zend_hash_index_find(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE],
+ (zend_ulong) opline, (void**)&brake) == SUCCESS) {
+ return (brake->base?(phpdbg_breakbase_t *)brake->base:(phpdbg_breakbase_t *)brake);
+ }
+
+ return NULL;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar opcode TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *brake;
+ const char *opname = phpdbg_decode_opcode(opcode);
+
+ if (memcmp(opname, PHPDBG_STRL("UNKNOWN")) == 0) {
+ return NULL;
+ }
+
+ if (zend_hash_index_find(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE],
+ zend_hash_func(opname, strlen(opname)), (void**)&brake) == SUCCESS) {
+ return brake;
+ }
+ return NULL;
+} /* }}} */
+
+static inline zend_bool phpdbg_find_breakpoint_param(phpdbg_param_t *param, zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+ zend_function *function = (zend_function*) execute_data->function_state.function;
+
+ switch (param->type) {
+ case NUMERIC_FUNCTION_PARAM:
+ case STR_PARAM: {
+ /* function breakpoint */
+
+ if (function->type != ZEND_USER_FUNCTION) {
+ return 0;
+ }
+
+ {
+ const char *str = NULL;
+ size_t len = 0L;
+ zend_op_array *ops = (zend_op_array*)function;
+ str = ops->function_name ? ops->function_name : "main";
+ len = strlen(str);
+
+ if (len == param->len && memcmp(param->str, str, len) == SUCCESS) {
+ return param->type == STR_PARAM || execute_data->opline - ops->opcodes == param->num;
+ }
+ }
+ } break;
+
+ case FILE_PARAM: {
+ if (param->file.line == zend_get_executed_lineno(TSRMLS_C)) {
+ const char *str = zend_get_executed_filename(TSRMLS_C);
+ size_t lengths[2] = {strlen(param->file.name), strlen(str)};
+
+ if (lengths[0] == lengths[1]) {
+ return (memcmp(
+ param->file.name, str, lengths[0]) == SUCCESS);
+ }
+ }
+ } break;
+
+ case NUMERIC_METHOD_PARAM:
+ case METHOD_PARAM: {
+ if (function->type != ZEND_USER_FUNCTION) {
+ return 0;
+ }
+
+ {
+ zend_op_array *ops = (zend_op_array*) function;
+
+ if (ops->scope) {
+ size_t lengths[2] = {strlen(param->method.class), ops->scope->name_length};
+ if (lengths[0] == lengths[1] && memcmp(param->method.class, ops->scope->name, lengths[0]) == SUCCESS) {
+ lengths[0] = strlen(param->method.name);
+ lengths[1] = strlen(ops->function_name);
+
+ if (lengths[0] == lengths[1] && memcmp(param->method.name, ops->function_name, lengths[0]) == SUCCESS) {
+ return param->type == METHOD_PARAM || (execute_data->opline - ops->opcodes) == param->num;
+ }
+ }
+ }
+ }
+ } break;
+
+ case ADDR_PARAM: {
+ return ((zend_ulong)(phpdbg_opline_ptr_t)execute_data->opline == param->addr);
+ } break;
+
+ default: {
+ /* do nothing */
+ } break;
+ }
+ return 0;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakcond_t *bp;
+ HashPosition position;
+ int breakpoint = FAILURE;
+
+ 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)) {
+ zval *retval = NULL;
+ zval **orig_retval = EG(return_value_ptr_ptr);
+ zend_op_array *orig_ops = EG(active_op_array);
+ zend_op **orig_opline = EG(opline_ptr);
+
+ if (((phpdbg_breakbase_t*)bp)->disabled) {
+ continue;
+ }
+
+ if (bp->paramed) {
+ if (!phpdbg_find_breakpoint_param(&bp->param, execute_data TSRMLS_CC)) {
+ continue;
+ }
+ }
+
+ ALLOC_INIT_ZVAL(retval);
+
+ EG(return_value_ptr_ptr) = &retval;
+ EG(active_op_array) = bp->ops;
+ EG(no_extensions) = 1;
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+ }
+
+ zend_try {
+ PHPDBG_G(flags) |= PHPDBG_IN_COND_BP;
+ zend_execute(EG(active_op_array) TSRMLS_CC);
+#if PHP_VERSION_ID >= 50700
+ if (zend_is_true(retval TSRMLS_CC)) {
+#else
+ if (zend_is_true(retval)) {
+#endif
+ breakpoint = SUCCESS;
+ }
+ } zend_catch {
+ EG(no_extensions)=1;
+ EG(return_value_ptr_ptr) = orig_retval;
+ EG(active_op_array) = orig_ops;
+ EG(opline_ptr) = orig_opline;
+ PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
+ } zend_end_try();
+
+ EG(no_extensions)=1;
+ EG(return_value_ptr_ptr) = orig_retval;
+ EG(active_op_array) = orig_ops;
+ EG(opline_ptr) = orig_opline;
+ PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
+
+ if (breakpoint == SUCCESS) {
+ break;
+ }
+ }
+
+ return (breakpoint == SUCCESS) ? ((phpdbg_breakbase_t*)bp) : NULL;
+} /* }}} */
+
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakpoint(zend_execute_data* execute_data TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *base = NULL;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED)) {
+ return NULL;
+ }
+
+ /* conditions cannot be executed by eval()'d code */
+ if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL) &&
+ (PHPDBG_G(flags) & PHPDBG_HAS_COND_BP) &&
+ (base = phpdbg_find_conditional_breakpoint(execute_data TSRMLS_CC))) {
+ goto result;
+ }
+
+ if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP) &&
+ (base = phpdbg_find_breakpoint_file(execute_data->op_array TSRMLS_CC))) {
+ goto result;
+ }
+
+ if (PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP)) {
+ /* check we are at the beginning of the stack */
+ if (execute_data->opline == EG(active_op_array)->opcodes) {
+ if ((base = phpdbg_find_breakpoint_symbol(
+ execute_data->function_state.function TSRMLS_CC))) {
+ goto result;
+ }
+ }
+ }
+
+ if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP) &&
+ (base = phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC))) {
+ goto result;
+ }
+
+ if ((PHPDBG_G(flags) & PHPDBG_HAS_OPCODE_BP) &&
+ (base = phpdbg_find_breakpoint_opcode(execute_data->opline->opcode TSRMLS_CC))) {
+ goto result;
+ }
+
+ return NULL;
+
+result:
+ /* we return nothing for disable breakpoints */
+ if (base->disabled) {
+ return NULL;
+ }
+
+ return base;
+} /* }}} */
+
+PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC) /* {{{ */
+{
+ HashTable **table;
+ HashPosition position;
+ phpdbg_breakbase_t *brake;
+
+ if ((brake = phpdbg_find_breakbase_ex(num, &table, &position TSRMLS_CC))) {
+ char *key;
+ zend_uint klen;
+ zend_ulong idx;
+ int type = brake->type;
+ char *name = NULL;
+ size_t name_len = 0L;
+
+ switch (type) {
+ case PHPDBG_BREAK_FILE:
+ case PHPDBG_BREAK_METHOD:
+ if (zend_hash_num_elements((*table)) == 1) {
+ name = estrdup(brake->name);
+ name_len = strlen(name);
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[type]) == 1) {
+ PHPDBG_G(flags) &= ~(1<<(brake->type+1));
+ }
+ }
+ break;
+
+ default: {
+ if (zend_hash_num_elements((*table)) == 1) {
+ PHPDBG_G(flags) &= ~(1<<(brake->type+1));
+ }
+ }
+ }
+
+ switch (type) {
+ case PHPDBG_BREAK_FILE_OPLINE:
+ case PHPDBG_BREAK_FUNCTION_OPLINE:
+ case PHPDBG_BREAK_METHOD_OPLINE:
+ if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]) == 1) {
+ PHPDBG_G(flags) &= PHPDBG_HAS_OPLINE_BP;
+ }
+ zend_hash_index_del(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], ((phpdbg_breakopline_t*)brake)->opline);
+ }
+
+ switch (zend_hash_get_current_key_ex(
+ (*table), &key, &klen, &idx, 0, &position)) {
+
+ case HASH_KEY_IS_STRING:
+ zend_hash_del((*table), key, klen);
+ break;
+
+ default:
+ zend_hash_index_del((*table), idx);
+ }
+
+ switch (type) {
+ case PHPDBG_BREAK_FILE:
+ case PHPDBG_BREAK_METHOD:
+ if (name) {
+ zend_hash_del(&PHPDBG_G(bp)[type], name, name_len);
+ efree(name);
+ }
+ break;
+ }
+
+ phpdbg_notice("breakpoint", "deleted=\"success\" id=\"%ld\"", "Deleted breakpoint #%ld", num);
+ PHPDBG_BREAK_UNMAPPING(num);
+ } else {
+ phpdbg_error("breakpoint", "type=\"nobreakpoint\" deleted=\"fail\" id=\"%ld\"", "Failed to find breakpoint #%ld", num);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D) /* {{{ */
+{
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+ zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
+
+ PHPDBG_G(flags) &= ~PHPDBG_BP_MASK;
+
+ PHPDBG_G(bp_count) = 0;
+} /* }}} */
+
+PHPDBG_API void phpdbg_hit_breakpoint(phpdbg_breakbase_t *brake, zend_bool output TSRMLS_DC) /* {{{ */
+{
+ brake->hits++;
+
+ if (output) {
+ phpdbg_print_breakpoint(brake TSRMLS_CC);
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_print_breakpoint(phpdbg_breakbase_t *brake TSRMLS_DC) /* {{{ */
+{
+ if (!brake)
+ goto unknown;
+
+ switch (brake->type) {
+ case PHPDBG_BREAK_FILE: {
+ phpdbg_notice("breakpoint", "id=\"%d\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d at %s:%ld, hits: %lu",
+ ((phpdbg_breakfile_t*)brake)->id,
+ ((phpdbg_breakfile_t*)brake)->filename,
+ ((phpdbg_breakfile_t*)brake)->line,
+ ((phpdbg_breakfile_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_SYM: {
+ phpdbg_notice("breakpoint", "id=\"%d\" function=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s() at %s:%u, hits: %lu",
+ ((phpdbg_breaksymbol_t*)brake)->id,
+ ((phpdbg_breaksymbol_t*)brake)->symbol,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakfile_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_OPLINE: {
+ phpdbg_notice("breakpoint", "id=\"%d\" opline=\"%#lx\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %#lx at %s:%u, hits: %lu",
+ ((phpdbg_breakline_t*)brake)->id,
+ ((phpdbg_breakline_t*)brake)->opline,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_METHOD_OPLINE: {
+ phpdbg_notice("breakpoint", "id=\"%d\" method=\"%s::%s\" num=\"%lu\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s::%s()#%lu at %s:%u, hits: %lu",
+ ((phpdbg_breakopline_t*)brake)->id,
+ ((phpdbg_breakopline_t*)brake)->class_name,
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakopline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_FUNCTION_OPLINE: {
+ phpdbg_notice("breakpoint", "id=\"%d\" num=\"%lu\" function=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s()#%lu at %s:%u, hits: %lu",
+ ((phpdbg_breakopline_t*)brake)->id,
+ ((phpdbg_breakopline_t*)brake)->func_name,
+ ((phpdbg_breakopline_t*)brake)->opline_num,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakopline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_FILE_OPLINE: {
+ phpdbg_notice("breakpoint", "id=\"%d\" num=\"%lu\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in #%lu at %s:%u, hits: %lu",
+ ((phpdbg_breakopline_t*)brake)->id,
+ ((phpdbg_breakopline_t*)brake)->opline_num,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakopline_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_OPCODE: {
+ phpdbg_notice("breakpoint", "id=\"%d\" opcode=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s at %s:%u, hits: %lu",
+ ((phpdbg_breakop_t*)brake)->id,
+ ((phpdbg_breakop_t*)brake)->name,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakop_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_METHOD: {
+ phpdbg_notice("breakpoint", "id=\"%d\" method=\"%s::%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s::%s() at %s:%u, hits: %lu",
+ ((phpdbg_breakmethod_t*)brake)->id,
+ ((phpdbg_breakmethod_t*)brake)->class_name,
+ ((phpdbg_breakmethod_t*)brake)->func_name,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakmethod_t*)brake)->hits);
+ } break;
+
+ case PHPDBG_BREAK_COND: {
+ if (((phpdbg_breakcond_t*)brake)->paramed) {
+ char *param;
+ phpdbg_notice("breakpoint", "id=\"%d\" location=\"%s\" eval=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Conditional breakpoint #%d: at %s if %s at %s:%u, hits: %lu",
+ ((phpdbg_breakcond_t*)brake)->id,
+ phpdbg_param_tostring(&((phpdbg_breakcond_t*)brake)->param, &param TSRMLS_CC),
+ ((phpdbg_breakcond_t*)brake)->code,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakcond_t*)brake)->hits);
+ if (param)
+ free(param);
+ } else {
+ phpdbg_notice("breakpoint", "id=\"%d\" eval=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Conditional breakpoint #%d: on %s == true at %s:%u, hits: %lu",
+ ((phpdbg_breakcond_t*)brake)->id,
+ ((phpdbg_breakcond_t*)brake)->code,
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C),
+ ((phpdbg_breakcond_t*)brake)->hits);
+ }
+
+ } break;
+
+ default: {
+unknown:
+ phpdbg_notice("breakpoint", "id=\"\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Unknown breakpoint at %s:%u",
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C));
+ }
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_enable_breakpoint(zend_ulong id TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *brake = phpdbg_find_breakbase(id TSRMLS_CC);
+
+ if (brake) {
+ brake->disabled = 0;
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_disable_breakpoint(zend_ulong id TSRMLS_DC) /* {{{ */
+{
+ phpdbg_breakbase_t *brake = phpdbg_find_breakbase(id TSRMLS_CC);
+
+ if (brake) {
+ brake->disabled = 1;
+ }
+} /* }}} */
+
+PHPDBG_API void phpdbg_enable_breakpoints(TSRMLS_D) /* {{{ */
+{
+ PHPDBG_G(flags) |= PHPDBG_IS_BP_ENABLED;
+} /* }}} */
+
+PHPDBG_API void phpdbg_disable_breakpoints(TSRMLS_D) { /* {{{ */
+ PHPDBG_G(flags) &= ~PHPDBG_IS_BP_ENABLED;
+} /* }}} */
+
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase(zend_ulong id TSRMLS_DC) /* {{{ */
+{
+ HashTable **table;
+ HashPosition position;
+
+ return phpdbg_find_breakbase_ex(id, &table, &position TSRMLS_CC);
+} /* }}} */
+
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase_ex(zend_ulong id, HashTable ***table, HashPosition *position TSRMLS_DC) /* {{{ */
+{
+ if (zend_hash_index_find(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], id, (void**)table) == SUCCESS) {
+ phpdbg_breakbase_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex((**table), position);
+ zend_hash_get_current_data_ex((**table), (void**)&brake, position) == SUCCESS;
+ zend_hash_move_forward_ex((**table), position)) {
+
+ if (brake->id == id) {
+ return brake;
+ }
+ }
+ }
+ return NULL;
+} /* }}} */
+
+PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC) /* {{{ */
+{
+ phpdbg_xml("<breakpoints %r>");
+
+ switch (type) {
+ case PHPDBG_BREAK_SYM: if ((PHPDBG_G(flags) & PHPDBG_HAS_SYM_BP)) {
+ HashPosition position;
+ phpdbg_breaksymbol_t *brake;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Function Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], &position)) {
+ phpdbg_writeln("function", "id=\"%d\" name=\"%s\" disabled=\"%s\"", "#%d\t\t%s%s",
+ brake->id, brake->symbol,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ } break;
+
+ case PHPDBG_BREAK_METHOD: if ((PHPDBG_G(flags) & PHPDBG_HAS_METHOD_BP)) {
+ HashPosition position[2];
+ HashTable *class_table;
+ char *class_name = NULL;
+ zend_uint class_len = 0;
+ zend_ulong class_idx = 0L;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Method Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], (void**) &class_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD],
+ &class_name, &class_len, &class_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+ phpdbg_breakmethod_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(class_table, &position[1]);
+ zend_hash_get_current_data_ex(class_table, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(class_table, &position[1])) {
+ phpdbg_writeln("method", "id=\"%d\" name=\"%s::%s\" disabled=\"%s\"", "#%d\t\t%s::%s%s",
+ brake->id, brake->class_name, brake->func_name,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+
+ }
+ } break;
+
+ case PHPDBG_BREAK_FILE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP)) {
+ HashPosition position[2];
+ HashTable *points;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("File Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], (void**) &points, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], &position[0])) {
+ phpdbg_breakfile_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(points, &position[1]);
+ zend_hash_get_current_data_ex(points, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(points, &position[1])) {
+ phpdbg_writeln("file", "id=\"%d\" name=\"%s\" line=\"%lu\" disabled=\"%s\"", "#%d\t\t%s:%lu%s",
+ brake->id, brake->filename, brake->line,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+
+ } break;
+
+ case PHPDBG_BREAK_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP)) {
+ HashPosition position;
+ phpdbg_breakline_t *brake;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Opline Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], &position)) {
+ switch (brake->type) {
+ case PHPDBG_BREAK_METHOD_OPLINE:
+ case PHPDBG_BREAK_FUNCTION_OPLINE:
+ case PHPDBG_BREAK_FILE_OPLINE:
+ phpdbg_writeln("opline", "id=\"%d\" num=\"%#lx\" type=\"%s\" disabled=\"%s\"", "#%d\t\t%#lx\t\t(%s breakpoint)%s", brake->id, brake->opline,
+ brake->type == PHPDBG_BREAK_METHOD_OPLINE ? "method" :
+ brake->type == PHPDBG_BREAK_FUNCTION_OPLINE ? "function" :
+ brake->type == PHPDBG_BREAK_FILE_OPLINE ? "file" :
+ "--- error ---",
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ default:
+ phpdbg_writeln("opline", "id=\"%d\" num=\"%#lx\" disabled=\"%s\"", "#%d\t\t%#lx%s", brake->id, brake->opline, ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+ }
+ }
+ } break;
+
+ case PHPDBG_BREAK_METHOD_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_METHOD_OPLINE_BP)) {
+ HashPosition position[3];
+ HashTable *class_table, *method_table;
+ char *class_name = NULL, *method_name = NULL;
+ zend_uint class_len = 0, method_len = 0;
+ zend_ulong class_idx = 0L, method_idx = 0L;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Method opline Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], (void**) &class_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE],
+ &class_name, &class_len, &class_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ for (zend_hash_internal_pointer_reset_ex(class_table, &position[1]);
+ zend_hash_get_current_data_ex(class_table, (void**) &method_table, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(class_table, &position[1])) {
+
+ if (zend_hash_get_current_key_ex(class_table,
+ &method_name, &method_len, &method_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ phpdbg_breakopline_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(method_table, &position[2]);
+ zend_hash_get_current_data_ex(method_table, (void**)&brake, &position[2]) == SUCCESS;
+ zend_hash_move_forward_ex(method_table, &position[2])) {
+ phpdbg_writeln("methodopline", "id=\"%d\" name=\"%s::%s\" num=\"%ld\" disabled=\"%s\"", "#%d\t\t%s::%s opline %ld%s",
+ brake->id, brake->class_name, brake->func_name, brake->opline_num,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+ }
+ }
+
+ }
+ } break;
+
+ case PHPDBG_BREAK_FUNCTION_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FUNCTION_OPLINE_BP)) {
+ HashPosition position[2];
+ HashTable *function_table;
+ char *function_name = NULL;
+ zend_uint function_len = 0;
+ zend_ulong function_idx = 0L;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Function opline Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], (void**) &function_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE],
+ &function_name, &function_len, &function_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ phpdbg_breakopline_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(function_table, &position[1]);
+ zend_hash_get_current_data_ex(function_table, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(function_table, &position[1])) {
+ phpdbg_writeln("functionopline", "id=\"%d\" name=\"%s\" num=\"%ld\" disabled=\"%s\"", "#%d\t\t%s opline %ld%s",
+ brake->id, brake->func_name, brake->opline_num,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+
+ }
+ } break;
+
+ case PHPDBG_BREAK_FILE_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_OPLINE_BP)) {
+ HashPosition position[2];
+ HashTable *file_table;
+ char *file_name = NULL;
+ zend_uint file_len = 0;
+ zend_ulong file_idx = 0L;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("File opline Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], &position[0]);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], (void**) &file_table, &position[0]) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], &position[0])) {
+
+ if (zend_hash_get_current_key_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE],
+ &file_name, &file_len, &file_idx, 0, &position[0]) == HASH_KEY_IS_STRING) {
+
+ phpdbg_breakopline_t *brake;
+
+ for (zend_hash_internal_pointer_reset_ex(file_table, &position[1]);
+ zend_hash_get_current_data_ex(file_table, (void**)&brake, &position[1]) == SUCCESS;
+ zend_hash_move_forward_ex(file_table, &position[1])) {
+ phpdbg_writeln("fileopline", "id=\"%d\" name=\"%s\" num=\"%ld\" disabled=\"%s\"", "#%d\t\t%s opline %ld%s",
+ brake->id, brake->class_name, brake->opline_num,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+ }
+ } break;
+
+ case PHPDBG_BREAK_COND: if ((PHPDBG_G(flags) & PHPDBG_HAS_COND_BP)) {
+ HashPosition position;
+ phpdbg_breakcond_t *brake;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Conditional Breakpoints:\n");
+ 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**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
+ if (brake->paramed) {
+ switch (brake->param.type) {
+ case STR_PARAM:
+ phpdbg_writeln("evalfunction", "id=\"%d\" name=\"%s\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s if %s%s",
+ brake->id,
+ brake->param.str,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ phpdbg_writeln("evalfunctionopline", "id=\"%d\" name=\"%s\" num=\"%ld\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s#%ld if %s%s",
+ brake->id,
+ brake->param.str,
+ brake->param.num,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case METHOD_PARAM:
+ phpdbg_writeln("evalmethod", "id=\"%d\" name=\"%s::%s\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s::%s if %s%s",
+ brake->id,
+ brake->param.method.class,
+ brake->param.method.name,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ phpdbg_writeln("evalmethodopline", "id=\"%d\" name=\"%s::%s\" num=\"%d\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s::%s#%ld if %s%s",
+ brake->id,
+ brake->param.method.class,
+ brake->param.method.name,
+ brake->param.num,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case FILE_PARAM:
+ phpdbg_writeln("evalfile", "id=\"%d\" name=\"%s\" line=\"%d\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s:%lu if %s%s",
+ brake->id,
+ brake->param.file.name,
+ brake->param.file.line,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ case ADDR_PARAM:
+ phpdbg_writeln("evalopline", "id=\"%d\" opline=\"%#lx\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat #%lx if %s%s",
+ brake->id,
+ brake->param.addr,
+ brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ break;
+
+ default:
+ phpdbg_error("eval", "type=\"invalidparameter\"", "Invalid parameter type for conditional breakpoint");
+ return;
+ }
+ } else {
+ phpdbg_writeln("eval", "id=\"%d\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tif %s%s",
+ brake->id, brake->code,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ }
+ } break;
+
+ case PHPDBG_BREAK_OPCODE: if (PHPDBG_G(flags) & PHPDBG_HAS_OPCODE_BP) {
+ HashPosition position;
+ phpdbg_breakop_t *brake;
+
+ phpdbg_out(SEPARATE "\n");
+ phpdbg_out("Opcode Breakpoints:\n");
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], (void**) &brake, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], &position)) {
+ phpdbg_writeln("opcode", "id=\"%d\" name=\"%s\" disabled=\"%s\"", "#%d\t\t%s%s",
+ brake->id, brake->name,
+ ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
+ }
+ } break;
+ }
+
+ phpdbg_xml("</breakpoints>");
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_bp.h b/sapi/phpdbg/phpdbg_bp.h
new file mode 100644
index 0000000000..a6227cba6c
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_bp.h
@@ -0,0 +1,159 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BP_H
+#define PHPDBG_BP_H
+
+/* {{{ defines */
+#define PHPDBG_BREAK_FILE 0
+#define PHPDBG_BREAK_SYM 1
+#define PHPDBG_BREAK_OPLINE 2
+#define PHPDBG_BREAK_METHOD 3
+#define PHPDBG_BREAK_COND 4
+#define PHPDBG_BREAK_OPCODE 5
+#define PHPDBG_BREAK_FUNCTION_OPLINE 6
+#define PHPDBG_BREAK_METHOD_OPLINE 7
+#define PHPDBG_BREAK_FILE_OPLINE 8
+#define PHPDBG_BREAK_MAP 9
+#define PHPDBG_BREAK_TABLES 10 /* }}} */
+
+/* {{{ */
+typedef struct _zend_op *phpdbg_opline_ptr_t; /* }}} */
+
+/* {{{ breakpoint base structure */
+#define phpdbg_breakbase(name) \
+ int id; \
+ zend_uchar type; \
+ zend_ulong hits; \
+ zend_bool disabled; \
+ const char *name /* }}} */
+
+/* {{{ breakpoint base */
+typedef struct _phpdbg_breakbase_t {
+ phpdbg_breakbase(name);
+} phpdbg_breakbase_t; /* }}} */
+
+/**
+ * Breakpoint file-based representation
+ */
+typedef struct _phpdbg_breakfile_t {
+ phpdbg_breakbase(filename);
+ long line;
+} phpdbg_breakfile_t;
+
+/**
+ * Breakpoint symbol-based representation
+ */
+typedef struct _phpdbg_breaksymbol_t {
+ phpdbg_breakbase(symbol);
+} phpdbg_breaksymbol_t;
+
+/**
+ * Breakpoint method based representation
+ */
+typedef struct _phpdbg_breakmethod_t {
+ phpdbg_breakbase(class_name);
+ size_t class_len;
+ const char *func_name;
+ size_t func_len;
+} phpdbg_breakmethod_t;
+
+/**
+ * Breakpoint opline num based representation
+ */
+typedef struct _phpdbg_breakopline_t {
+ phpdbg_breakbase(func_name);
+ size_t func_len;
+ const char *class_name;
+ size_t class_len;
+ zend_ulong opline_num;
+ zend_ulong opline;
+} phpdbg_breakopline_t;
+
+/**
+ * Breakpoint opline based representation
+ */
+typedef struct _phpdbg_breakline_t {
+ phpdbg_breakbase(name);
+ zend_ulong opline;
+ phpdbg_breakopline_t *base;
+} phpdbg_breakline_t;
+
+/**
+ * Breakpoint opcode based representation
+ */
+typedef struct _phpdbg_breakop_t {
+ phpdbg_breakbase(name);
+ zend_ulong hash;
+} phpdbg_breakop_t;
+
+/**
+ * Breakpoint condition based representation
+ */
+typedef struct _phpdbg_breakcond_t {
+ phpdbg_breakbase(code);
+ size_t code_len;
+ zend_bool paramed;
+ phpdbg_param_t param;
+ zend_ulong hash;
+ zend_op_array *ops;
+} phpdbg_breakcond_t;
+
+/* {{{ Opline breaks API */
+PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC);
+PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC);
+PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC); /* }}} */
+
+/* {{{ Breakpoint Creation API */
+PHPDBG_API void phpdbg_set_breakpoint_file(const char* filename, long lineno TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_symbol(const char* func_name, size_t func_name_len TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_method(const char* class_name, const char* func_name TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_opcode(const char* opname, size_t opname_len TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, zend_ulong opline TSRMLS_DC);
+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 TSRMLS_DC); /* }}} */
+
+/* {{{ Breakpoint Detection API */
+PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
+
+/* {{{ Misc Breakpoint API */
+PHPDBG_API void phpdbg_hit_breakpoint(phpdbg_breakbase_t* brake, zend_bool output TSRMLS_DC);
+PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC);
+PHPDBG_API void phpdbg_print_breakpoint(phpdbg_breakbase_t* brake TSRMLS_DC);
+PHPDBG_API void phpdbg_reset_breakpoints(TSRMLS_D);
+PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D);
+PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC);
+PHPDBG_API void phpdbg_enable_breakpoints(TSRMLS_D);
+PHPDBG_API void phpdbg_enable_breakpoint(zend_ulong id TSRMLS_DC);
+PHPDBG_API void phpdbg_disable_breakpoint(zend_ulong id TSRMLS_DC);
+PHPDBG_API void phpdbg_disable_breakpoints(TSRMLS_D); /* }}} */
+
+/* {{{ Breakbase API */
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase(zend_ulong id TSRMLS_DC);
+PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase_ex(zend_ulong id, HashTable ***table, HashPosition *position TSRMLS_DC); /* }}} */
+
+/* {{{ Breakpoint Exportation API */
+PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC); /* }}} */
+
+#endif /* PHPDBG_BP_H */
diff --git a/sapi/phpdbg/phpdbg_break.c b/sapi/phpdbg/phpdbg_break.c
new file mode 100644
index 0000000000..386d4d9562
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_break.c
@@ -0,0 +1,55 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_print.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_break.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s, flags) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10], flags)
+
+/**
+ * Commands
+ */
+const phpdbg_command_t phpdbg_break_commands[] = {
+ PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c", 0),
+ PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n", 0),
+ PHPDBG_END_COMMAND
+};
+
+PHPDBG_BREAK(at) /* {{{ */
+{
+ phpdbg_set_breakpoint_at(param TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_BREAK(del) /* {{{ */
+{
+ phpdbg_delete_breakpoint(param->num TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_break.h b/sapi/phpdbg/phpdbg_break.h
new file mode 100644
index 0000000000..dc06da62b7
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_break.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BREAK_H
+#define PHPDBG_BREAK_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_BREAK(name) PHPDBG_COMMAND(break_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_BREAK(at);
+PHPDBG_BREAK(del);
+
+extern const phpdbg_command_t phpdbg_break_commands[];
+
+#endif /* PHPDBG_BREAK_H */
diff --git a/sapi/phpdbg/phpdbg_btree.c b/sapi/phpdbg/phpdbg_btree.c
new file mode 100644
index 0000000000..491445399b
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_btree.c
@@ -0,0 +1,221 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg_btree.h"
+#include "phpdbg.h"
+
+#define CHOOSE_BRANCH(n) \
+ branch = branch->branches[!!(n)];
+
+#ifdef _Win32
+# define emalloc malloc
+# define efree free
+#endif
+
+/* depth in bits */
+void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) {
+ tree->depth = depth;
+ tree->branch = NULL;
+ tree->count = 0;
+}
+
+phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx) {
+ phpdbg_btree_branch *branch = tree->branch;
+ int i = tree->depth - 1;
+
+ if (branch == NULL) {
+ return NULL;
+ }
+
+ do {
+ if ((idx >> i) % 2 == 1) {
+ if (branch->branches[1]) {
+ CHOOSE_BRANCH(1);
+ } else {
+ return NULL;
+ }
+ } else {
+ if (branch->branches[0]) {
+ CHOOSE_BRANCH(0);
+ } else {
+ return NULL;
+ }
+ }
+ } while (i--);
+
+ return &branch->result;
+}
+
+phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx) {
+ phpdbg_btree_branch *branch = tree->branch;
+ int i = tree->depth - 1, last_superior_i = -1;
+ zend_bool had_alternative_branch = 0;
+
+ if (branch == NULL) {
+ return NULL;
+ }
+
+ /* find nearest watchpoint */
+ do {
+ /* an impossible branch was found if: */
+ if (!had_alternative_branch && (idx >> i) % 2 == 0 && !branch->branches[0]) {
+ /* there's no lower branch than idx */
+ if (last_superior_i == -1) {
+ /* failure */
+ return NULL;
+ }
+ /* reset state */
+ branch = tree->branch;
+ i = tree->depth - 1;
+ /* follow branch according to bits in idx until the last lower branch before the impossible branch */
+ do {
+ CHOOSE_BRANCH((idx >> i) % 2 == 1 && branch->branches[1]);
+ } while (--i > last_superior_i);
+ /* use now the lower branch of which we can be sure that it contains only branches lower than idx */
+ CHOOSE_BRANCH(0);
+ /* and choose the highest possible branch in the branch containing only branches lower than idx */
+ while (i--) {
+ CHOOSE_BRANCH(branch->branches[1]);
+ }
+ break;
+ }
+ /* follow branch according to bits in idx until having found an impossible branch */
+ if (had_alternative_branch || (idx >> i) % 2 == 1) {
+ if (branch->branches[1]) {
+ if (branch->branches[0]) {
+ last_superior_i = i;
+ }
+ CHOOSE_BRANCH(1);
+ } else {
+ CHOOSE_BRANCH(0);
+ had_alternative_branch = 1;
+ }
+ } else {
+ CHOOSE_BRANCH(0);
+ }
+ } while (i--);
+
+ return &branch->result;
+}
+
+phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx) {
+ phpdbg_btree_position pos;
+
+ pos.tree = tree;
+ pos.end = lower_idx;
+ pos.cur = higher_idx;
+
+ return pos;
+}
+
+phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos) {
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(pos->tree, pos->cur);
+
+ if (result == NULL || result->idx < pos->end) {
+ return NULL;
+ }
+
+ pos->cur = result->idx - 1;
+
+ return result;
+}
+
+int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags) {
+ int i = tree->depth - 1;
+ phpdbg_btree_branch **branch = &tree->branch;
+
+ do {
+ if (*branch == NULL) {
+ break;
+ }
+ branch = &(*branch)->branches[(idx >> i) % 2];
+ } while (i--);
+
+ if (*branch == NULL) {
+ if (!(flags & PHPDBG_BTREE_INSERT)) {
+ return FAILURE;
+ }
+
+ {
+ phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch));
+ do {
+ (*branch)->branches[!((idx >> i) % 2)] = NULL;
+ branch = &(*branch)->branches[(idx >> i) % 2];
+ *branch = ++memory;
+ } while (i--);
+ tree->count++;
+ }
+ } else if (!(flags & PHPDBG_BTREE_UPDATE)) {
+ return FAILURE;
+ }
+
+ (*branch)->result.idx = idx;
+ (*branch)->result.ptr = ptr;
+
+ return SUCCESS;
+}
+
+int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) {
+ int i = tree->depth;
+ phpdbg_btree_branch *branch = tree->branch;
+ int i_last_dual_branch = -1, last_dual_branch_branch;
+ phpdbg_btree_branch *last_dual_branch = NULL;
+
+ goto check_branch_existence;
+ do {
+ if (branch->branches[0] && branch->branches[1]) {
+ last_dual_branch = branch;
+ i_last_dual_branch = i;
+ last_dual_branch_branch = (idx >> i) % 2;
+ }
+ branch = branch->branches[(idx >> i) % 2];
+
+check_branch_existence:
+ if (branch == NULL) {
+ return FAILURE;
+ }
+ } while (i--);
+
+ tree->count--;
+
+ if (i_last_dual_branch == -1) {
+ efree(tree->branch);
+ tree->branch = NULL;
+ } else {
+ if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) {
+ phpdbg_btree_branch *original_branch = last_dual_branch->branches[!last_dual_branch_branch];
+
+ memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], (i_last_dual_branch + 1) * sizeof(phpdbg_btree_branch));
+ efree(last_dual_branch->branches[!last_dual_branch_branch]);
+ last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1;
+
+ branch = last_dual_branch->branches[!last_dual_branch_branch];
+ for (i = i_last_dual_branch; i--;) {
+ branch = (branch->branches[branch->branches[1] == ++original_branch] = last_dual_branch + i_last_dual_branch - i + 1);
+ }
+ } else {
+ efree(last_dual_branch->branches[last_dual_branch_branch]);
+ }
+
+ last_dual_branch->branches[last_dual_branch_branch] = NULL;
+ }
+
+ return SUCCESS;
+}
diff --git a/sapi/phpdbg/phpdbg_btree.h b/sapi/phpdbg/phpdbg_btree.h
new file mode 100644
index 0000000000..af2a6ac314
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_btree.h
@@ -0,0 +1,65 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BTREE_H
+#define PHPDBG_BTREE_H
+
+#include "zend.h"
+
+typedef struct {
+ zend_ulong idx;
+ void *ptr;
+} phpdbg_btree_result;
+
+typedef union _phpdbg_btree_branch phpdbg_btree_branch;
+union _phpdbg_btree_branch {
+ phpdbg_btree_branch *branches[2];
+ phpdbg_btree_result result;
+};
+
+typedef struct {
+ zend_ulong count;
+ zend_ulong depth;
+ phpdbg_btree_branch *branch;
+} phpdbg_btree;
+
+typedef struct {
+ phpdbg_btree *tree;
+ zend_ulong cur;
+ zend_ulong end;
+} phpdbg_btree_position;
+
+void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth);
+phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx);
+phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx);
+phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx);
+phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos);
+int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx);
+
+#define PHPDBG_BTREE_INSERT 1
+#define PHPDBG_BTREE_UPDATE 2
+#define PHPDBG_BTREE_OVERWRITE (PHPDBG_BTREE_INSERT | PHPDBG_BTREE_UPDATE)
+
+int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags);
+#define phpdbg_btree_insert(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_INSERT)
+#define phpdbg_btree_update(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_UPDATE)
+#define phpdbg_btree_overwrite(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_OWERWRITE)
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_cmd.c b/sapi/phpdbg/phpdbg_cmd.c
new file mode 100644
index 0000000000..30ea48cfbe
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_cmd.c
@@ -0,0 +1,924 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_set.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_io.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:
+ return "address";
+ case NUMERIC_PARAM:
+ return "numeric";
+ case METHOD_PARAM:
+ return "method";
+ case NUMERIC_FUNCTION_PARAM:
+ return "function opline";
+ case NUMERIC_METHOD_PARAM:
+ return "method opline";
+ case FILE_PARAM:
+ return "file or file opline";
+ case STR_PARAM:
+ return "string";
+ default: /* this is bad */
+ return "unknown";
+ }
+}
+
+PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char *str, size_t len, phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ char *class_name, *func_name;
+
+ if (len == 0) {
+ param->type = EMPTY_PARAM;
+ goto parsed;
+ }
+
+ if (phpdbg_is_addr(str)) {
+ param->addr = strtoul(str, 0, 16);
+ param->type = ADDR_PARAM;
+ goto parsed;
+
+ } else if (phpdbg_is_numeric(str)) {
+ param->num = strtol(str, NULL, 0);
+ param->type = NUMERIC_PARAM;
+ goto parsed;
+
+ } else if (phpdbg_is_class_method(str, len+1, &class_name, &func_name)) {
+ param->method.class = class_name;
+ param->method.name = func_name;
+ param->type = METHOD_PARAM;
+ goto parsed;
+ } else {
+ char *line_pos = strrchr(str, ':');
+
+ if (line_pos && phpdbg_is_numeric(line_pos+1)) {
+ if (strchr(str, ':') == line_pos) {
+ char path[MAXPATHLEN];
+
+ memcpy(path, str, line_pos - str);
+ path[line_pos - str] = 0;
+ *line_pos = 0;
+ param->file.name = phpdbg_resolve_path(path TSRMLS_CC);
+ param->file.line = strtol(line_pos+1, NULL, 0);
+ param->type = FILE_PARAM;
+
+ goto parsed;
+ }
+ }
+
+ line_pos = strrchr(str, '#');
+
+ if (line_pos && phpdbg_is_numeric(line_pos+1)) {
+ if (strchr(str, '#') == line_pos) {
+ param->num = strtol(line_pos + 1, NULL, 0);
+
+ if (phpdbg_is_class_method(str, line_pos - str, &class_name, &func_name)) {
+ param->method.class = class_name;
+ param->method.name = func_name;
+ param->type = NUMERIC_METHOD_PARAM;
+ } else {
+ param->len = line_pos - str;
+ param->str = estrndup(str, param->len);
+ param->type = NUMERIC_FUNCTION_PARAM;
+ }
+
+ goto parsed;
+ }
+ }
+ }
+
+ param->str = estrndup(str, len);
+ param->len = len;
+ param->type = STR_PARAM;
+
+parsed:
+ phpdbg_debug("phpdbg_parse_param(\"%s\", %lu): %s",
+ str, len, phpdbg_get_param_type(param TSRMLS_CC));
+ return param->type;
+} /* }}} */
+
+PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ if (param) {
+ switch (param->type) {
+ case FILE_PARAM:
+ efree(param->file.name);
+ break;
+ case METHOD_PARAM:
+ efree(param->method.class);
+ efree(param->method.name);
+ break;
+ case STR_PARAM:
+ efree(param->str);
+ break;
+ default:
+ break;
+ }
+ }
+
+} /* }}} */
+
+PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ asprintf(pointer,
+ "%s", param->str);
+ break;
+
+ case ADDR_PARAM:
+ asprintf(pointer,
+ "%#lx", param->addr);
+ break;
+
+ case NUMERIC_PARAM:
+ asprintf(pointer,
+ "%li",
+ param->num);
+ break;
+
+ case METHOD_PARAM:
+ asprintf(pointer,
+ "%s::%s",
+ param->method.class,
+ param->method.name);
+ break;
+
+ case FILE_PARAM:
+ if (param->num) {
+ asprintf(pointer,
+ "%s:%lu#%lu",
+ param->file.name,
+ param->file.line,
+ param->num);
+ } else {
+ asprintf(pointer,
+ "%s:%lu",
+ param->file.name,
+ param->file.line);
+ }
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ asprintf(pointer,
+ "%s#%lu", param->str, param->num);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ asprintf(pointer,
+ "%s::%s#%lu",
+ param->method.class,
+ param->method.name,
+ param->num);
+ break;
+
+ default:
+ asprintf(pointer,
+ "%s", "unknown");
+ }
+
+ return *pointer;
+} /* }}} */
+
+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;
+ break;
+
+ case NUMERIC_PARAM:
+ dest->num = src->num;
+ break;
+
+ case METHOD_PARAM:
+ dest->method.class = estrdup(src->method.class);
+ 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;
+ if (src->num)
+ dest->num = src->num;
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ dest->str = estrndup(src->str, src->len);
+ dest->num = src->num;
+ dest->len = src->len;
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ dest->method.class = estrdup(src->method.class);
+ dest->method.name = estrdup(src->method.name);
+ dest->num = src->num;
+ break;
+
+ case EMPTY_PARAM: { /* do nothing */ } break;
+
+ default: {
+ /* not yet */
+ }
+ }
+} /* }}} */
+
+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;
+
+ case METHOD_PARAM:
+ hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
+ hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
+ break;
+
+ case FILE_PARAM:
+ hash += zend_inline_hash_func(param->file.name, strlen(param->file.name));
+ hash += param->file.line;
+ if (param->num)
+ hash += param->num;
+ break;
+
+ case ADDR_PARAM:
+ hash += param->addr;
+ break;
+
+ case NUMERIC_PARAM:
+ hash += param->num;
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ hash += zend_inline_hash_func(param->str, param->len);
+ hash += param->num;
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
+ hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
+ if (param->num)
+ hash+= param->num;
+ break;
+
+ case EMPTY_PARAM: { /* do nothing */ } break;
+
+ default: {
+ /* not yet */
+ }
+ }
+
+ return hash;
+} /* }}} */
+
+PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r TSRMLS_DC) /* {{{ */
+{
+ 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;
+ }
+ /* break intentionally omitted */
+
+ case STR_PARAM:
+ return (l->len == r->len) &&
+ (memcmp(l->str, r->str, l->len) == SUCCESS);
+
+ case NUMERIC_PARAM:
+ return (l->num == r->num);
+
+ case ADDR_PARAM:
+ return (l->addr == r->addr);
+
+ case FILE_PARAM: {
+ if (l->file.line == r->file.line) {
+ size_t lengths[2] = {
+ strlen(l->file.name), strlen(r->file.name)};
+
+ if (lengths[0] == lengths[1]) {
+ if ((!l->num && !r->num) || (l->num == r->num)) {
+ return (memcmp(
+ l->file.name, r->file.name, lengths[0]) == SUCCESS);
+ }
+ }
+ }
+ } break;
+
+ case NUMERIC_METHOD_PARAM:
+ if (l->num != r->num) {
+ break;
+ }
+ /* break intentionally omitted */
+
+ case METHOD_PARAM: {
+ size_t lengths[2] = {
+ strlen(l->method.class), strlen(r->method.class)};
+ if (lengths[0] == lengths[1]) {
+ if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) {
+ lengths[0] = strlen(l->method.name);
+ lengths[1] = strlen(r->method.name);
+
+ if (lengths[0] == lengths[1]) {
+ return (memcmp(
+ l->method.name, r->method.name, lengths[0]) == SUCCESS);
+ }
+ }
+ }
+ } break;
+
+ case EMPTY_PARAM:
+ return 1;
+
+ default: {
+ /* not yet */
+ }
+ }
+ }
+ }
+ return 0;
+} /* }}} */
+
+/* {{{ */
+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 */
+ }
+ }
+ }
+} /* }}} */
+
+/* {{{ */
+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 */
+ }
+ }
+
+ free(remove);
+ remove = NULL;
+
+ if (next)
+ remove = next;
+ else break;
+ }
+ }
+
+
+ stack->next = NULL;
+} /* }}} */
+
+/* {{{ */
+PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) {
+ phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t));
+
+ if (!next)
+ return;
+
+ *(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;
+ }
+
+ stack->len++;
+} /* }}} */
+
+PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack 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;
+ }
+
+ phpdbg_error("command", "type=\"toomanyargs\" command=\"%s\" expected=\"0\"", "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) { \
+ phpdbg_error("command", "type=\"noarg\" command=\"%s\" expected=\"%s\" num=\"%lu\"", "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)) { \
+ phpdbg_error("command", "type=\"wrongarg\" command=\"%s\" expected=\"%s\" got=\"%s\" num=\"%lu\"", "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)) {
+ phpdbg_error("command", "type=\"toofewargs\" command=\"%s\" expected=\"%d\" argtypes=\"%s\" got=\"%d\"", "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 TSRMLS_DC) {
+ 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 {
+ /* 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) {
+ phpdbg_error("command", "type=\"notfound\" command=\"%s\" subcommand=\"%s\"", "The command \"%s %s\" could not be found", parent->name, name->str);
+ } else {
+ phpdbg_error("command", "type=\"notfound\" command=\"%s\"", "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 = emalloc(matched[it]->name_len + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0));
+ } else {
+ list = erealloc(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++;
+ }
+
+ /* ", " separated matches */
+ phpdbg_error("command", "type=\"ambiguous\" command=\"%s\" matches=\"%lu\" matched=\"%s\"", "The command \"%s\" is ambigious, matching %lu commands (%s)", name->str, matches, list);
+ efree(list);
+
+ return NULL;
+ }
+ }
+
+ if (command->subs && (*top) && ((*top)->type == STR_PARAM)) {
+ return phpdbg_stack_resolve(command->subs, command, top TSRMLS_CC);
+ } else {
+ return command;
+ }
+
+ return NULL;
+} /* }}} */
+
+/* {{{ */
+PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, zend_bool allow_async_unsafe TSRMLS_DC) {
+ phpdbg_param_t *top = NULL;
+ const phpdbg_command_t *handler = NULL;
+
+ if (stack->type != STACK_PARAM) {
+ phpdbg_error("command", "type=\"nostack\"", "The passed argument was not a stack !");
+ return FAILURE;
+ }
+
+ if (!stack->len) {
+ phpdbg_error("command", "type=\"emptystack\"", "The stack contains nothing !");
+ return FAILURE;
+ }
+
+ top = (phpdbg_param_t *) stack->next;
+
+ switch (top->type) {
+ case EVAL_PARAM:
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+ return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC);
+
+ case RUN_PARAM:
+ if (!allow_async_unsafe) {
+ phpdbg_error("signalsegv", "command=\"run\"", "run command is disallowed during hard interrupt");
+ }
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+ return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC);
+
+ case SHELL_PARAM:
+ if (!allow_async_unsafe) {
+ phpdbg_error("signalsegv", "command=\"sh\"", "sh command is disallowed during hard interrupt");
+ return FAILURE;
+ }
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+ return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC);
+
+ case STR_PARAM: {
+ handler = phpdbg_stack_resolve(phpdbg_prompt_commands, NULL, &top TSRMLS_CC);
+
+ if (handler) {
+ if (!allow_async_unsafe && !(handler->flags & PHPDBG_ASYNC_SAFE)) {
+ phpdbg_error("signalsegv", "command=\"%s\"", "%s command is disallowed during hard interrupt", handler->name);
+ return FAILURE;
+ }
+
+ if (phpdbg_stack_verify(handler, &top TSRMLS_CC) == SUCCESS) {
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+ return handler->handler(top TSRMLS_CC);
+ }
+ }
+ } return FAILURE;
+
+ default:
+ phpdbg_error("command", "type=\"invalidcommand\"", "The first parameter makes no sense !");
+ return FAILURE;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API char *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
+{
+ char *cmd = NULL;
+ char *buffer = NULL;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && (buffered == NULL) && !phpdbg_active_sigsafe_mem(TSRMLS_C)) {
+ fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
+ }
+
+ if (buffered == NULL) {
+ if (0) {
+disconnect:
+ PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
+ zend_bailout();
+ return NULL;
+ }
+
+#define USE_LIB_STAR (defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT))
+
+ /* note: EOF makes readline write prompt again in local console mode - and ignored if compiled without readline */
+ /* strongly assuming to be in blocking mode... */
+#if USE_LIB_STAR
+readline:
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE)
+#endif
+ {
+ char buf[PHPDBG_MAX_CMD];
+ int bytes = PHPDBG_G(input_buflen), len = 0;
+ if (PHPDBG_G(input_buflen)) {
+ memcpy(buf, PHPDBG_G(input_buffer), bytes);
+ }
+
+ phpdbg_write("prompt", "", "%s", phpdbg_get_prompt(TSRMLS_C));
+ PHPDBG_G(last_was_newline) = 1;
+
+ do {
+ int i;
+ if (bytes <= 0) {
+ continue;
+ }
+
+ for (i = len; i < len + bytes; i++) {
+ if (buf[i] == '\x03') {
+ if (i != len + bytes - 1) {
+ memmove(buf + i, buf + i + 1, len + bytes - i - 1);
+ }
+ len--;
+ i--;
+ continue;
+ }
+ if (buf[i] == '\n') {
+ PHPDBG_G(input_buflen) = len + bytes - 1 - i;
+ if (PHPDBG_G(input_buflen)) {
+ memcpy(PHPDBG_G(input_buffer), buf + i + 1, PHPDBG_G(input_buflen));
+ }
+ if (i != PHPDBG_MAX_CMD - 1) {
+ buf[i + 1] = 0;
+ }
+ cmd = buf;
+ goto end;
+ }
+ }
+ len += bytes;
+ /* XXX export the timeout through INI??*/
+ } while ((bytes = phpdbg_mixed_read(PHPDBG_G(io)[PHPDBG_STDIN].fd, buf + len, PHPDBG_MAX_CMD - len, -1 TSRMLS_CC)) > 0);
+
+ if (bytes <= 0) {
+ goto disconnect;
+ }
+
+ cmd = buf;
+ }
+#if USE_LIB_STAR
+ else {
+ cmd = readline(phpdbg_get_prompt(TSRMLS_C));
+ }
+
+ if (!cmd) {
+ goto readline;
+ }
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ add_history(cmd);
+ }
+#endif
+ } else {
+ cmd = buffered;
+ }
+end:
+ PHPDBG_G(last_was_newline) = 1;
+ buffer = estrdup(cmd);
+
+#if USE_LIB_STAR
+ if (!buffered && cmd && !(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ free(cmd);
+ }
+#endif
+ }
+
+ if (buffer && isspace(*buffer)) {
+ char *trimmed = buffer;
+ while (isspace(*trimmed))
+ trimmed++;
+
+ trimmed = estrdup(trimmed);
+ efree(buffer);
+ buffer = trimmed;
+ }
+
+ 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));
+ }
+ }
+
+ return buffer;
+} /* }}} */
+
+PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */
+{
+ efree(*input);
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_cmd.h b/sapi/phpdbg/phpdbg_cmd.h
new file mode 100644
index 0000000000..3896551c9a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_cmd.h
@@ -0,0 +1,184 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_CMD_H
+#define PHPDBG_CMD_H
+
+#include "TSRM.h"
+
+/* {{{ Command and Parameter */
+enum {
+ NO_ARG = 0,
+ REQUIRED_ARG,
+ OPTIONAL_ARG
+};
+
+typedef enum {
+ EMPTY_PARAM = 0,
+ ADDR_PARAM,
+ FILE_PARAM,
+ NUMERIC_FILE_PARAM,
+ METHOD_PARAM,
+ STR_PARAM,
+ NUMERIC_PARAM,
+ NUMERIC_FUNCTION_PARAM,
+ NUMERIC_METHOD_PARAM,
+ STACK_PARAM,
+ EVAL_PARAM,
+ SHELL_PARAM,
+ COND_PARAM,
+ OP_PARAM,
+ ORIG_PARAM,
+ RUN_PARAM
+} phpdbg_param_type;
+
+typedef struct _phpdbg_param phpdbg_param_t;
+struct _phpdbg_param {
+ phpdbg_param_type type;
+ long num;
+ zend_ulong addr;
+ struct {
+ char *name;
+ long line;
+ } file;
+ struct {
+ char *class;
+ char *name;
+ } method;
+ char *str;
+ size_t len;
+ phpdbg_param_t *next;
+ phpdbg_param_t *top;
+};
+
+#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
+
+#define PHPDBG_ASYNC_SAFE 1
+
+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 */
+ const char *tip; /* Menu tip */
+ size_t tip_len; /* Menu tip length */
+ char alias; /* Alias */
+ phpdbg_command_handler_t handler; /* Command handler */
+ const phpdbg_command_t *subs; /* Sub Commands */
+ char *args; /* Argument Spec */
+ const phpdbg_command_t *parent; /* Parent Command */
+ zend_bool flags; /* General flags */
+};
+/* }}} */
+
+/* {{{ misc */
+#define PHPDBG_STRL(s) s, sizeof(s)-1
+#define PHPDBG_MAX_CMD 500
+#define PHPDBG_FRAME(v) (PHPDBG_G(frame).v)
+#define PHPDBG_EX(v) (EG(current_execute_data)->v)
+
+typedef struct {
+ int num;
+ zend_execute_data *execute_data;
+} phpdbg_frame_t;
+/* }}} */
+
+/*
+* Workflow:
+* 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 char* phpdbg_read_input(char *buffered TSRMLS_DC);
+PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC);
+
+/**
+ * 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 TSRMLS_DC);
+PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack TSRMLS_DC);
+PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, zend_bool allow_async_unsafe TSRMLS_DC);
+PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack);
+
+/*
+* Parameter Management
+*/
+PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char*, size_t, phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_clear_param(phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t*, phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_param_t * TSRMLS_DC);
+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);
+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_EXP(name, tip, alias, handler, children, args, parent, flags) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent, flags}
+
+#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args, flags) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL, flags}
+
+#define PHPDBG_COMMAND_D(name, tip, alias, children, args, flags) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL, flags}
+
+#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC)
+
+#define PHPDBG_COMMAND_ARGS param TSRMLS_CC
+
+#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, NULL, NULL, 0}
+
+/*
+* Default Switch Case
+*/
+#define phpdbg_default_switch_case() \
+ default: \
+ phpdbg_error("command", "type=\"wrongarg\" got=\"%s\"", "Unsupported parameter type (%s) for command", phpdbg_get_param_type(param TSRMLS_CC)); \
+ break
+
+#endif /* PHPDBG_CMD_H */
diff --git a/sapi/phpdbg/phpdbg_eol.c b/sapi/phpdbg/phpdbg_eol.c
new file mode 100644
index 0000000000..fc20d567bc
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_eol.c
@@ -0,0 +1,172 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "phpdbg.h"
+#include "phpdbg_eol.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+#define EOL_LIST_LEN 4
+struct phpdbg_eol_rep phpdbg_eol_list[EOL_LIST_LEN] = {
+ {"CRLF", "\r\n", PHPDBG_EOL_CRLF},
+/* {"LFCR", "\n\r", PHPDBG_EOL_LFCR},*/
+ {"LF", "\n", PHPDBG_EOL_LF},
+ {"CR", "\r", PHPDBG_EOL_CR},
+};
+
+int phpdbg_eol_global_update(char *name TSRMLS_DC)
+{
+
+ if (0 == memcmp(name, "CRLF", 4) || 0 == memcmp(name, "crlf", 4) || 0 == memcmp(name, "DOS", 3) || 0 == memcmp(name, "dos", 3)) {
+ PHPDBG_G(eol) = PHPDBG_EOL_CRLF;
+ } else if (0 == memcmp(name, "LF", 2) || 0 == memcmp(name, "lf", 2) || 0 == memcmp(name, "UNIX", 4) || 0 == memcmp(name, "unix", 4)) {
+ PHPDBG_G(eol) = PHPDBG_EOL_LF;
+ } else if (0 == memcmp(name, "CR", 2) || 0 == memcmp(name, "cr", 2) || 0 == memcmp(name, "MAC", 3) || 0 == memcmp(name, "mac", 3)) {
+ PHPDBG_G(eol) = PHPDBG_EOL_CR;
+ } else {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+char *phpdbg_eol_name(int id)
+{
+ size_t i = 0;
+
+ while (i < EOL_LIST_LEN) {
+
+ if (id == phpdbg_eol_list[i].id) {
+ return phpdbg_eol_list[i].name;
+ }
+
+ i++;
+ }
+
+ return NULL;
+}
+
+char *phpdbg_eol_rep(int id)
+{
+ size_t i = 0;
+
+ while (i < EOL_LIST_LEN) {
+
+ if (id == phpdbg_eol_list[i].id) {
+ return phpdbg_eol_list[i].rep;
+ }
+
+ i++;
+ }
+
+ return NULL;
+}
+
+
+/* Inspired by https://ccrma.stanford.edu/~craig/utility/flip/flip.cpp */
+void phpdbg_eol_convert(char **str, int *len TSRMLS_DC)
+{
+ char *in = *str, *out ;
+ int in_len = *len, out_len, cursor, i;
+ char last, cur;
+
+ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) != PHPDBG_IS_REMOTE) {
+ return;
+ }
+
+ out_len = *len;
+ if (PHPDBG_EOL_CRLF == PHPDBG_G(eol)) { /* XXX add LFCR case if it's gonna be needed */
+ /* depending on the source EOL the out str will have all CR/LF duplicated */
+ for (i = 0; i < in_len; i++) {
+ if (0x0a == in[i] || 0x0d == in[i]) {
+ out_len++;
+ }
+ }
+ out = (char *)emalloc(out_len);
+
+ last = cur = in[0];
+ i = cursor = 0;
+ for (; i < in_len;) {
+ if (0x0a == cur && last != 0x0d) {
+ out[cursor] = 0x0d;
+ cursor++;
+ out[cursor] = cur;
+ } else if(0x0d == cur) {
+ if (i + 1 < in_len && 0x0a != in[i+1]) {
+ out[cursor] = cur;
+ cursor++;
+ out[cursor] = 0x0a;
+ last = 0x0a;
+ } else {
+ out[cursor] = 0x0d;
+ last = 0x0d;
+ }
+ } else {
+ out[cursor] = cur;
+ last = cur;
+ }
+
+ i++;
+ cursor++;
+ cur = in[i];
+ }
+
+ } else if (PHPDBG_EOL_LF == PHPDBG_G(eol) || PHPDBG_EOL_CR == PHPDBG_G(eol)) {
+ char want, kick;
+
+ if (PHPDBG_EOL_LF == PHPDBG_G(eol)) {
+ want = 0x0a;
+ kick = 0x0d;
+ } else {
+ want = 0x0d;
+ kick = 0x0a;
+ }
+
+ /* We gonna have a smaller or equally long string, estimation is almost neglecting */
+ out = (char *)emalloc(out_len);
+
+ last = cur = in[0];
+ i = cursor = 0;
+ for (; cursor < in_len;) {
+ if (kick == cur) {
+ out[cursor] = want;
+ } else if (want == cur) {
+ if (kick != last) {
+ out[cursor] = want;
+ }
+ } else {
+ out[cursor] = cur;
+ }
+
+ last = cur;
+ cursor++;
+ cur = in[cursor];
+ }
+ } else {
+ return;
+ }
+
+ efree(*str);
+ *str = erealloc(out, cursor);
+ *len = cursor;
+ in = NULL;
+}
diff --git a/sapi/phpdbg/phpdbg_eol.h b/sapi/phpdbg/phpdbg_eol.h
new file mode 100644
index 0000000000..68b54cbe34
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_eol.h
@@ -0,0 +1,46 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_EOL_H
+#define PHPDBG_EOL_H
+
+#include "phpdbg.h"
+
+struct phpdbg_eol_rep {
+ char *name;
+ char *rep;
+ int id;
+};
+
+enum {
+ PHPDBG_EOL_CRLF, /* DOS */
+ /*PHPDBG_EOL_LFCR,*/ /* for Risc OS? */
+ PHPDBG_EOL_LF, /* UNIX */
+ PHPDBG_EOL_CR /* MAC */
+};
+
+int phpdbg_eol_global_update(char *name TSRMLS_DC);
+
+char *phpdbg_eol_name(int id);
+
+char *phpdbg_eol_rep(int id);
+
+void phpdbg_eol_convert(char **str, int *len TSRMLS_DC);
+
+#endif /* PHPDBG_EOL_H */
+
diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c
new file mode 100644
index 0000000000..437e6d474a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_frame.c
@@ -0,0 +1,252 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_frame.h"
+#include "phpdbg_list.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+ZEND_EXTERN_MODULE_GLOBALS(output);
+
+void phpdbg_restore_frame(TSRMLS_D) /* {{{ */
+{
+ if (PHPDBG_FRAME(num) == 0) {
+ return;
+ }
+
+ PHPDBG_FRAME(num) = 0;
+
+ /* move things back */
+ EG(current_execute_data) = PHPDBG_FRAME(execute_data);
+
+ EG(opline_ptr) = &PHPDBG_EX(opline);
+ EG(active_op_array) = PHPDBG_EX(op_array);
+ EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value);
+ EG(active_symbol_table) = PHPDBG_EX(symbol_table);
+ EG(This) = PHPDBG_EX(current_this);
+ EG(scope) = PHPDBG_EX(current_scope);
+ EG(called_scope) = PHPDBG_EX(current_called_scope);
+} /* }}} */
+
+void phpdbg_switch_frame(int frame TSRMLS_DC) /* {{{ */
+{
+ zend_execute_data *execute_data = PHPDBG_FRAME(num)?PHPDBG_FRAME(execute_data):EG(current_execute_data);
+ int i = 0;
+
+ if (PHPDBG_FRAME(num) == frame) {
+ phpdbg_notice("frame", "id=\"%d\"", "Already in frame #%d", frame);
+ return;
+ }
+
+ phpdbg_try_access {
+ while (execute_data) {
+ if (i++ == frame) {
+ break;
+ }
+
+ do {
+ execute_data = execute_data->prev_execute_data;
+ } while (execute_data && execute_data->opline == NULL);
+ }
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Couldn't switch frames, invalid data source");
+ return;
+ } phpdbg_end_try_access();
+
+ if (execute_data == NULL) {
+ phpdbg_error("frame", "type=\"maxnum\" id=\"%d\"", "No frame #%d", frame);
+ return;
+ }
+
+ phpdbg_restore_frame(TSRMLS_C);
+
+ if (frame > 0) {
+ PHPDBG_FRAME(num) = frame;
+
+ /* backup things and jump back */
+ PHPDBG_FRAME(execute_data) = EG(current_execute_data);
+ EG(current_execute_data) = execute_data;
+
+ EG(opline_ptr) = &PHPDBG_EX(opline);
+ EG(active_op_array) = PHPDBG_EX(op_array);
+ PHPDBG_FRAME(execute_data)->original_return_value = EG(return_value_ptr_ptr);
+ EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value);
+ EG(active_symbol_table) = PHPDBG_EX(symbol_table);
+ EG(This) = PHPDBG_EX(current_this);
+ EG(scope) = PHPDBG_EX(current_scope);
+ EG(called_scope) = PHPDBG_EX(current_called_scope);
+ }
+
+ phpdbg_notice("frame", "id=\"%d\"", "Switched to frame #%d", frame);
+ phpdbg_list_file(
+ zend_get_executed_filename(TSRMLS_C),
+ 3,
+ zend_get_executed_lineno(TSRMLS_C)-1,
+ zend_get_executed_lineno(TSRMLS_C)
+ TSRMLS_CC
+ );
+} /* }}} */
+
+static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */
+{
+ zval **funcname, **class, **type, **args, **argstmp;
+ char is_class;
+ int has_args = FAILURE;
+
+ zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"), (void **) &funcname);
+
+ if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "object", sizeof("object"), (void **) &class)) == FAILURE) {
+ is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"), (void **)&class);
+ } else {
+ zend_get_object_classname(*class, (const char **) &Z_STRVAL_PP(class), (zend_uint *) &Z_STRLEN_PP(class) TSRMLS_CC);
+ }
+
+ if (is_class == SUCCESS) {
+ zend_hash_find(Z_ARRVAL_PP(tmp), "type", sizeof("type"), (void **)&type);
+ }
+
+ has_args = zend_hash_find(Z_ARRVAL_PP(tmp), "args", sizeof("args"), (void **)&args) == SUCCESS;
+
+ phpdbg_xml(" symbol=\"%s%s%s\"",
+ is_class == FAILURE?"":Z_STRVAL_PP(class),
+ is_class == FAILURE?"":Z_STRVAL_PP(type),
+ Z_STRVAL_PP(funcname)
+ );
+ if (has_args) {
+ phpdbg_xml(">");
+ } else {
+ phpdbg_xml(" />");
+ }
+
+ phpdbg_out("%s%s%s(",
+ is_class == FAILURE?"":Z_STRVAL_PP(class),
+ is_class == FAILURE?"":Z_STRVAL_PP(type),
+ Z_STRVAL_PP(funcname)
+ );
+
+ if (has_args) {
+ HashPosition iterator;
+ const zend_function *func = NULL;
+ const zend_arg_info *arginfo = NULL;
+ int j = 0, m;
+ zend_bool is_variadic = 0;
+
+ phpdbg_try_access {
+ /* assuming no autoloader call is necessary, class should have been loaded if it's in backtrace ... */
+ if ((func = phpdbg_get_function(Z_STRVAL_PP(funcname), is_class == FAILURE ? NULL : Z_STRVAL_PP(class) TSRMLS_CC))) {
+ arginfo = func->common.arg_info;
+ }
+ } phpdbg_end_try_access();
+
+ m = func ? func->common.num_args : 0;
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(args), &iterator);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args), (void **) &argstmp, &iterator) == SUCCESS) {
+ if (j) {
+ phpdbg_out(", ");
+ }
+ phpdbg_xml("<arg %r");
+ if (m && j < m) {
+#if PHP_VERSION_ID >= 50600
+ is_variadic = arginfo ? arginfo[j].is_variadic : 0;
+#endif
+ phpdbg_xml(" variadic=\"%s\" name=\"%s\">", is_variadic ? "variadic" : "", arginfo ? arginfo[j].name : "");
+ phpdbg_out("%s=%s", arginfo ? arginfo[j].name : "?", is_variadic ? "[": "");
+
+ } else {
+ phpdbg_xml(">");
+ }
+ ++j;
+
+ zend_print_flat_zval_r(*argstmp TSRMLS_CC);
+ zend_hash_move_forward_ex(Z_ARRVAL_PP(args), &iterator);
+
+ phpdbg_xml("</arg>");
+ }
+ if (is_variadic) {
+ phpdbg_out("]");
+ }
+ phpdbg_xml("</frame>");
+ }
+ phpdbg_out(")");
+}
+
+void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
+{
+ zval zbacktrace;
+ zval **tmp;
+ zval **file, **line;
+ HashPosition position;
+ int i = 0, limit = num;
+ int user_defined;
+
+ PHPDBG_OUTPUT_BACKUP();
+
+ if (limit < 0) {
+ phpdbg_error("backtrace", "type=\"minnum\"", "Invalid backtrace size %d", limit);
+
+ PHPDBG_OUTPUT_BACKUP_RESTORE();
+ return;
+ }
+
+ phpdbg_try_access {
+ zend_fetch_debug_backtrace(&zbacktrace, 0, 0, limit TSRMLS_CC);
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Couldn't fetch backtrace, invalid data source");
+ return;
+ } phpdbg_end_try_access();
+
+ phpdbg_xml("<backtrace %r>");
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL(zbacktrace), &position);
+ zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void **) &tmp, &position);
+ while (1) {
+ user_defined = zend_hash_find(Z_ARRVAL_PP(tmp), "file", sizeof("file"), (void **) &file);
+ zend_hash_find(Z_ARRVAL_PP(tmp), "line", sizeof("line"), (void **) &line);
+ zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position);
+
+ if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void **) &tmp, &position) == FAILURE) {
+ phpdbg_write("frame", "id=\"%d\" symbol=\"{main}\" file=\"%s\" line=\"%d\"", "frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ break;
+ }
+
+ if (user_defined == SUCCESS) {
+ phpdbg_out("frame #%d: ", i);
+ phpdbg_xml("<frame %r id=\"%d\" file=\"%s\" line=\"%d\"", i, Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ phpdbg_dump_prototype(tmp TSRMLS_CC);
+ phpdbg_out(" at %s:%ld\n", Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ i++;
+ } else {
+ phpdbg_out(" => ");
+ phpdbg_xml("<frame %r id=\"%d\" internal=\"internal\"", i);
+ phpdbg_dump_prototype(tmp TSRMLS_CC);
+ phpdbg_out(" (internal function)\n");
+ }
+ }
+
+ phpdbg_out("\n");
+ phpdbg_xml("</backtrace>");
+
+ zval_dtor(&zbacktrace);
+
+ PHPDBG_OUTPUT_BACKUP_RESTORE();
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_frame.h b/sapi/phpdbg/phpdbg_frame.h
new file mode 100644
index 0000000000..7c4574ed28
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_frame.h
@@ -0,0 +1,30 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_FRAME_H
+#define PHPDBG_FRAME_H
+
+#include "TSRM.h"
+
+void phpdbg_restore_frame(TSRMLS_D);
+void phpdbg_switch_frame(int TSRMLS_DC);
+void phpdbg_dump_backtrace(size_t TSRMLS_DC);
+
+#endif /* PHPDBG_FRAME_H */
diff --git a/sapi/phpdbg/phpdbg_help.c b/sapi/phpdbg/phpdbg_help.c
new file mode 100644
index 0000000000..652e170694
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_help.c
@@ -0,0 +1,945 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ | Authors: Terry Ellison <terry@ellisons.org.uk> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_help.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_eol.h"
+#include "zend.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+/* {{{ 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)
+{
+ 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 */
+
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ phpdbg_xml("<help %r msg=\"%s\" />", text);
+ return;
+ }
+
+ /* First pass calculates a safe size for the pretty print version */
+ for (p = text; *p; p++) {
+ if (UNEXPECTED(p[0] == '*') && p[1] == '*') {
+ size += bold_escape_len - 2;
+ p++;
+ } else if (UNEXPECTED(p[0] == '$') && p[1] == 'P') {
+ size += prompt_escape_len - 2;
+ p++;
+ } else if (UNEXPECTED(p[0] == '\\')) {
+ p++;
+ }
+ }
+ size += (p-text)+1;
+
+ new = emalloc(size);
+ /*
+ * Second pass substitutes the bold and prompt escape sequences and line wrap
+ *
+ * ** toggles bold on and off if PHPDBG_IS_COLOURED flag is set
+ * $P substitutes the prompt sequence
+ * Lines are wrapped by replacing the last blank with a CR before <term width>
+ * characters. (This defaults to 100 if the width can't be detected). In the
+ * pathelogical case where no blanks are found, then the wrap occurs at the
+ * first blank.
+ */
+ for (p = text, q = new; *p; p++) {
+ if (UNEXPECTED(*p == ' ')) {
+ last_new_blank = q;
+ last_blank_count = line_count++;
+ *q++ = ' ';
+ } else if (UNEXPECTED(*p == '\n')) {
+ last_new_blank = NULL;
+ *q++ = *p;
+ last_blank_count = 0;
+ line_count = 0;
+ } else if (UNEXPECTED(p[0] == '*') && p[1] == '*') {
+ if (bold_escape_len) {
+ in_bold = !in_bold;
+ memcpy (q, in_bold ? bold_on_escape : bold_off_escape, bold_escape_len);
+ q += bold_escape_len;
+ /* bold on/off has zero print width so line count is unchanged */
+ }
+ p++;
+ } else if (UNEXPECTED(p[0] == '$') && p[1] == 'P') {
+ memcpy (q, prompt_escape, prompt_escape_len);
+ q += prompt_escape_len;
+ line_count += prompt_len;
+ p++;
+ } else if (UNEXPECTED(p[0] == '\\')) {
+ p++;
+ *q++ = *p;
+ line_count++;
+ } else {
+ *q++ = *p;
+ line_count++;
+ }
+
+ 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';
+
+ if ((q-new)>size) {
+ phpdbg_error("help", "overrun=\"%lu\"", "Output overrun of %lu bytes", ((q - new) - size));
+ }
+
+ phpdbg_out("%s\n", new);
+ efree(new);
+} /* }}} */
+
+/* {{{ summary_print. Print a summary line giving, the command, its alias and tip */
+void summary_print(phpdbg_command_t const * const cmd TSRMLS_DC)
+{
+ 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_text_t *p;
+
+ /* 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 */
+
+ for (p = phpdbg_help_text; p->key; p++) {
+ if (!strcmp(p->key, key)) {
+ return p->text;
+ }
+ }
+ return ""; /* return empty string to denote no match found */
+} /* }}} */
+
+/* {{{ 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)
+{
+ 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;
+ }
+ }
+ }
+ } else {
+ for (c=commands; c->name; c++) {
+ if (!strncmp(c->name, key, len)) {
+ ++num_matches;
+ if ( num_matches == 1 && command) {
+ *command = c;
+ }
+ }
+ }
+ }
+
+ return num_matches;
+
+} /* }}} */
+
+PHPDBG_COMMAND(help) /* {{{ */
+{
+ phpdbg_command_t const *cmd;
+ int n;
+
+ if (!param || param->type == EMPTY_PARAM) {
+ pretty_print(get_help("overview!" TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+ }
+
+ 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_error("help", "type=\"ambiguousalias\" alias=\"%s\"", "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;
+ }
+ }
+ }
+ }
+
+ return FAILURE;
+
+} /* }}} */
+
+PHPDBG_HELP(aliases) /* {{{ */
+{
+ const phpdbg_command_t *c, *c_sub;
+ int len;
+
+ /* Print out aliases for all commands except help as this one comes last */
+ phpdbg_writeln("help", "", "Below are the aliased, short versions of all supported commands");
+ phpdbg_xml("<helpcommands %r>");
+ for(c = phpdbg_prompt_commands; c->name; c++) {
+ if (c->alias && c->alias != 'h') {
+ phpdbg_writeln("command", "alias=\"%c\" name=\"%s\" tip=\"%s\"", " %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("subcommand", "parent_alias=\"%c\" alias=\"%c\" parent=\"%s\" name=\"%-*s\" tip=\"%s\"", " %c %c %s %-*s %s",
+ c->alias, c_sub->alias, c->name, len, c_sub->name, c_sub->tip);
+ }
+ }
+ }
+ }
+ }
+
+ phpdbg_xml("</helpcommands>");
+
+ /* 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("aliasinfo", "alias=\"%c\" name=\"%s\" tip=\"%s\"", " %c %-20s %s\n", c->alias, c->name, c->tip);
+
+ phpdbg_xml("<helpaliases>");
+
+ len = 20 - 1 - c->name_len;
+ for(c_sub = c->subs; c_sub->alias; c_sub++) {
+ if (c_sub->alias) {
+ phpdbg_writeln("alias", "parent_alias=\"%c\" alias=\"%c\" parent=\"%s\" name=\"%-*s\" tip=\"%s\"", " %c %c %s %-*s %s",
+ c->alias, c_sub->alias, c->name, len, c_sub->name, c_sub->tip);
+ }
+ }
+
+ phpdbg_xml("</helpaliases>");
+
+ pretty_print(get_help("aliases!" TSRMLS_CC) TSRMLS_CC);
+ 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
+" **back** shows the current backtrace" CR
+" **help** provide help on a topic" CR CR
+
+"**Starting and Stopping Execution**" CR
+" **exec** set execution context" CR
+" **run** attempt execution" CR
+" **step** continue execution until other line is reached" CR
+" **continue** continue execution" CR
+" **until** continue execution up to the given location" CR
+" **finish** continue up to end of the current execution frame" CR
+" **leave** continue up to end of the current execution frame and halt after the calling instruction" CR
+" **break** set a breakpoint at the specified target" CR
+" **watch** set a watchpoint on $variable" CR
+" **clear** clear one or all breakpoints" CR
+" **clean** clean the execution environment" CR CR
+
+"**Miscellaneous**" CR
+" **set** set the phpdbg configuration" CR
+" **source** execute a phpdbginit script" CR
+" **register** register a phpdbginit function as a command alias" CR
+" **sh** shell a command" CR
+" **ev** evaluate some code" CR
+" **quit** exit phpdbg" CR CR
+
+"Type **help <command>** or (**help alias**) to get detailed help on any of the above commands, "
+"for example **help list** or **h l**. Note that help will also match partial commands if unique "
+"(and list out options if not unique), so **help clea** will give help on the **clean** command, "
+"but **help cl** will list the summary for **clean** and **clear**." CR CR
+
+"Type **help aliases** to show a full alias list, including any registered phpdginit functions" CR
+"Type **help syntax** for a general introduction to the command syntax." CR
+"Type **help options** for a list of phpdbg command line options." CR
+"Type **help phpdbginit** to show how to customise the debugger environment."
+},
+{"options", CR
+"Below are the command line options supported by phpdbg" CR CR
+ /* note the extra 4 space index in because of the extra **** */
+"**Command Line Options and Flags**" CR
+" **Option** **Example Argument** **Description**" CR
+" **-c** **-c**/my/php.ini Set php.ini file to load" CR
+" **-d** **-d**memory_limit=4G Set a php.ini directive" CR
+" **-n** Disable default php.ini" CR
+" **-q** Suppress 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
+" **-x** Enable xml output (instead of normal text output)" 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."
+},
+
+{"phpdbginit", CR
+"Phpdgb uses an debugger script file to initialize the debugger context. By default, phpdbg looks "
+"for the file named **.phpdbginit** in the current working directory. This location can be "
+"overridden on the command line using the **-i** switch (see **help options** for a more "
+"details)." CR CR
+
+"Debugger scripts can also be executed using the **source** command." CR CR
+
+"A script file can contain a sequence of valid debugger commands, comments and embedded PHP "
+"code. " CR CR
+
+"Comment lines are prefixed by the **#** character. Note that comments are only allowed in script "
+"files and not in interactive sessions." CR CR
+
+"PHP code is delimited by the start and end escape tags **<:** and **:>**. PHP code can be used "
+"to define application context for a debugging session and also to extend the debugger by defining "
+"and **register** PHP functions as new commands." CR CR
+
+"Also note that executing a **clear** command will cause the current **phpdbginit** to be reparsed "
+"/ reloaded."
+},
+
+{"syntax", CR
+"Commands start with a keyword, and some (**break**, "
+"**info**, **set**, **print** and **list**) may include a subcommand keyword. All keywords are "
+"lower case but also have a single letter alias that may be used as an alternative to typing in the"
+"keyword in full. Note some aliases are uppercase, and that keywords cannot be abbreviated other "
+"than by substitution by the alias." CR CR
+
+"Some commands take an argument. Arguments are typed according to their format:" CR
+" * **omitted**" CR
+" * **address** **0x** followed by a hex string" CR
+" * **number** an optionally signed number" CR
+" * **method** a valid **Class::methodName** expression" CR
+" * **func#op** a valid **Function name** follow by # and an integer" CR
+" * **method#op** a valid **Class::methodName** follow by # and an integer" CR
+" * **string** a general string" CR
+" * **function** a valid **Function name**" CR
+" * **file:line** a valid **filename** follow by : and an integer" CR CR
+
+"In some cases the type of the argument enables the second keyword to be omitted." CR CR
+
+"Type **help** for an overview of all commands and type **help <command>** to get detailed help "
+"on any specific command." CR CR
+
+"**Valid Examples**" CR CR
+
+" $P quit" CR
+" $P q" CR
+" Quit the debugger" CR CR
+
+" $P ev $total[2]" CR
+" Evaluate and print the variable $total[2] in the current stack frame" CR
+" " CR
+" $P break 200" CR
+" $P b my_source.php:200" CR
+" Break at line 200 in the current source and in file **my_source.php**. " CR CR
+
+" $P b @ ClassX::get_args if $arg[0] == \"fred\"" CR
+" $P b ~ 3" CR
+" Break at ClassX::get_args() if $arg[0] == \"fred\" and delete breakpoint 3" CR CR
+
+"**Examples of invalid commands**" CR
+
+" $P #This is a comment" CR
+" Comments introduced by the **#** character are only allowed in **phpdbginit** script files."
+},
+
+/******************************** Help Codicils ********************************/
+{"aliases!", CR
+"Note that aliases can be used for either command or sub-command keywords or both, so **info b** "
+"is a synomyn for **info break** and **l func** for **list func**, etc." CR CR
+
+"Note that help will also accept any alias as a parameter and provide help on that command, for example **h p** will provide help on the print command."
+},
+
+{"duplicate!", CR
+"Parameter is not unique. For detailed help select help on one of the above commands."
+},
+
+/******************************** Help on Commands ********************************/
+{"back",
+"Provide a formatted backtrace using the standard debug_backtrace() functionality. An optional "
+"unsigned integer argument specifying the maximum number of frames to be traced; if omitted then "
+"a complete backtrace is given." CR CR
+
+"**Examples**" CR CR
+" $P back 5" CR
+" $P t " CR
+" " CR
+"A backtrace can be executed at any time during execution."
+},
+
+{"break",
+"Breakpoints can be set at a range of targets within the execution environment. Execution will "
+"be paused if the program flow hits a breakpoint. The break target can be one of the following "
+"types:" CR CR
+
+" **Target** **Alias** **Purpose**" CR
+" **at** **A** specify breakpoint by location and condition" CR
+" **del** **d** delete breakpoint by breakpoint identifier number" CR CR
+
+"**Break at** takes two arguments. The first is any valid target. The second "
+"is a valid PHP expression which will trigger the break in "
+"execution, if evaluated as true in a boolean context at the specified target." CR CR
+
+"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
+
+" $P break my_function#14" CR
+" $P b my_function#14" CR
+" Break at the opline #14 of the function my_function" CR CR
+
+" $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 occurrence 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 until "
+"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 ev $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 functions" CR
+" **error** **e** show last error" CR
+" **constants** **d** show user-defined constants" CR
+" **vars** **v** show active variables" CR
+" **globals** **g** show superglobal variables" CR
+" **literal** **l** show active literal constants" CR
+" **memory** **m** show memory manager stats"
+},
+
+// ******** same issue about breakpoints in called frames
+{"leave",
+"The **leave** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered within the current stack frame will be skipped. In effect a "
+"temporary breakpoint is associated with any return opcode, so that a break in execution occurs "
+"before leaving the current stack frame. This allows inspection / modification of any frame "
+"variables including the return value before it is returned" CR CR
+
+"**Examples**" CR CR
+
+" $P leave" CR
+" $P L" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current stack frame are also "
+"skipped. "CR CR
+
+"Note **leave** will trigger a \"not executing\" error if not executing."
+},
+
+{"list",
+"The list command displays source code for the given argument. The target type is specficied by "
+"a second subcommand keyword:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **lines** **l** List N lines from the current execution point" CR
+" **func** **f** List the complete source for a specified function" CR
+" **method** **m** List the complete source for a specified class::method" CR
+" **class** **c** List the complete source for a specified class" CR CR
+
+"Note that the context of **lines**, **func** and **method** can be determined by parsing the "
+"argument, so these subcommands are optional. However, you must specify the **class** keyword "
+"to list off a class." CR CR
+
+"**Examples**" CR CR
+" $P list 2" CR
+" $P l l 2" CR
+" List the next 2 lines from the current file" CR CR
+
+" $P list my_function" CR
+" $P l f my_function" CR
+" List the source of the function **my_function**" CR CR
+
+//************ ????
+" $P list func .mine" CR
+" $P l f .mine" CR
+" List the source of the method **mine** from the active class in scope" CR CR
+
+" $P list m my::method" CR
+" $P l my::method" CR
+" List the source of **my::method**" CR CR
+
+" $P list c myClass" CR
+" $P l c myClass" CR
+" List the source of **myClass**" CR CR
+
+"Note that functions and classes can only be listed if the corresponding classes and functions "
+"table in the Zend executor has a corresponding entry. You can use the compile command to "
+"populate these tables for a given execution context."
+},
+
+{"continue",
+"Continue with execution after hitting a break or watchpoint" CR CR
+
+"**Examples**" CR CR
+" $P continue" CR
+" $P c" CR
+" Continue executing until the next break or watchpoint" CR CR
+
+"Note **continue** will trigger a \"not running\" error if not executing."
+},
+
+{"print",
+"By default, print will show information about the current execution context." CR
+"Other printing commands give access to instruction information." CR
+"Specific printers loaded are show below:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **exec** **e** print out the instructions in the execution context" CR
+" **opline** **o** print out the instruction in the current opline" CR
+" **class** **c** print out the instructions in the specified class" CR
+" **method** **m** print out the instructions in the specified method" CR
+" **func** **f** print out the instructions in the specified function" CR
+" **stack** **s** print out the instructions in the current stack" CR CR
+
+"**Examples**" CR CR
+" $P print class \\\\my\\\\class" CR
+" $P p c \\\\my\\\\class" CR
+" Print the instructions for the methods in \\\\my\\\\class" CR CR
+
+" $P print method \\\\my\\\\class::method" CR
+" $P p m \\\\my\\\\class::method" CR
+" Print the instructions for \\\\my\\\\class::method" CR CR
+
+" $P print func .getSomething" CR
+" $P p f .getSomething" CR
+//************* Check this local method scope
+" Print the instructions for ::getSomething in the active scope" CR CR
+
+" $P print func my_function" CR
+" $P p f my_function" CR
+" Print the instructions for the global function my_function" CR CR
+
+" $P print opline" CR
+" $P p o" CR
+" Print the instruction for the current opline" CR CR
+
+" $P print exec" CR
+" $P p e" CR
+" Print the instructions for the execution context" CR CR
+
+" $P print stack" CR
+" $P p s" CR
+" Print the instructions for the current stack"
+},
+
+{"register",
+//******* Needs a general explanation of the how registered functions work
+"Register any global function for use as a command in phpdbg console" CR CR
+
+"**Examples**" CR CR
+" $P register scandir" CR
+" $P R scandir" CR
+" Will register the scandir function for use in phpdbg" CR CR
+
+"Note: arguments passed as strings, return (if present) print_r'd on console"
+},
+
+{"run",
+"Enter the vm, startinging execution. Execution will then continue until the next breakpoint "
+"or completion of the script. Add parameters you want to use as $argv"
+"**Examples**" CR CR
+" $P run" CR
+" $P r" CR
+" Will cause execution of the context, if it is set" CR CR
+" $P r test" CR
+" Will execute with $argv[1] == \"test\"" CR CR
+
+"Note that the execution context must be set. If not previously compiled, then the script will "
+"be compiled before execution." CR CR
+
+"Note that attempting to run a script that is already executing will result in an \"execution "
+"in progress\" error."
+},
+
+{"set",
+"The **set** command is used to configure how phpdbg looks and behaves. Specific set commands "
+"are as follows:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **prompt** **p** set the prompt" CR
+" **color** **c** set color <element> <color>" CR
+" **colors** **C** set colors [<on|off>]" CR
+" **oplog** **O** set oplog [output]" CR
+" **break** **b** set break **id** <on|off>" CR
+" **breaks** **B** set breaks [<on|off>]" CR
+" **quiet** **q** set quiet [<on|off>]" CR
+" **stepping** **s** set stepping [<opcode|line>]" CR
+" **refcount** **r** set refcount [<on|off>] " CR CR
+
+"Valid colors are **none**, **white**, **red**, **green**, **yellow**, **blue**, **purple**, "
+"**cyan** and **black**. All colours except **none** can be followed by an optional "
+"**-bold** or **-underline** qualifier." CR CR
+
+"Color elements can be one of **prompt**, **notice**, or **error**." CR CR
+
+"**Examples**" CR CR
+" $P S C on" CR
+" Set colors on" CR CR
+
+" $P set p >" CR
+" $P set color prompt white-bold" CR
+" Set the prompt to a bold >" CR CR
+
+" $P S c error red-bold" CR
+" Use red bold for errors" CR CR
+
+" $P S refcount on" CR
+" Enable refcount display when hitting watchpoints" CR CR
+
+" $P S b 4 off" CR
+" Temporarily disable breakpoint 4. This can be subsequently reenabled by a **s b 4 on**." CR
+//*********** check oplog syntax
+},
+
+{"sh",
+"Direct access to shell commands saves having to switch windows/consoles" CR CR
+
+"**Examples**" CR CR
+" $P sh ls /usr/src/php-src" CR
+" Will execute ls /usr/src/php-src, displaying the output in the console"
+//*********** what does this mean????Note: read only commands please!
+},
+
+{"source",
+"Sourcing a **phpdbginit** script during your debugging session might save some time." CR CR
+
+"**Examples**" CR CR
+
+" $P source /my/init" CR
+" $P < /my/init" CR
+" Will execute the phpdbginit file at /my/init" CR CR
+},
+
+{"export",
+"Exporting breakpoints allows you to share, and or save your current debugging session" CR CR
+
+"**Examples**" CR CR
+
+" $P export /my/exports" CR
+" $P > /my/exports" CR
+" Will export all breakpoints to /my/exports" CR CR
+},
+
+{"step",
+"Execute opcodes until next line" CR CR
+
+"**Examples**" CR CR
+
+" $P s" CR
+" Will continue and break again in the next encountered line" CR CR
+},
+
+{"until",
+"The **until** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered before the next source line will be skipped. Execution "
+"will then continue until the next breakpoint or completion of the script" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current line are also skipped. "CR CR
+
+"Note that if the next line is **not** executed then **all** subsequent breakpoints will be "
+"skipped. " CR CR
+
+"Note **until** will trigger a \"not executing\" error if not executing."
+
+},
+{"watch",
+"Sets watchpoints on variables as long as they are defined" CR
+"Passing no parameter to **watch**, lists all actually active watchpoints" CR CR
+
+"**Format for $variable**" CR CR
+" **$var** Variable $var" CR
+" **$var[]** All array elements of $var" CR
+" **$var->** All properties of $var" CR
+" **$var->a** Property $var->a" CR
+" **$var[b]** Array element with key b in array $var" CR CR
+
+"Subcommands of **watch**:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **array** **a** Sets watchpoint on array/object to observe if an entry is added or removed" CR
+" **recursive** **r** Watches variable recursively and automatically adds watchpoints if some entry is added to an array/object" CR
+" **delete** **d** Removes watchpoint" CR CR
+
+"Note when **recursive** watchpoints are removed, watchpoints on all the children are removed too" CR CR
+
+"**Examples**" CR CR
+" $P watch" CR
+" List currently active watchpoints" CR CR
+
+" $P watch $array" CR
+" $P w $array" CR
+" Set watchpoint on $array" CR CR
+
+" $P watch recursive $obj->" CR
+" $P w r $obj->" CR
+" Set recursive watchpoint on $obj->" CR CR
+
+" $P watch delete $obj->a" CR
+" $P w d $obj->a" CR
+" Remove watchpoint $obj->a" CR CR
+
+"Technical note: If using this feature with a debugger, you will get many segmentation faults, each time when a memory page containing a watched address is hit." CR
+" You then you can continue, phpdbg will remove the write protection, so that the program can continue." CR
+" If phpdbg could not handle that segfault, the same segfault is triggered again and this time phpdbg will abort."
+},
+{NULL, NULL /* end of table marker */}
+}; /* }}} */
diff --git a/sapi/phpdbg/phpdbg_help.h b/sapi/phpdbg/phpdbg_help.h
new file mode 100644
index 0000000000..4a433fda6c
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_help.h
@@ -0,0 +1,48 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_HELP_H
+#define PHPDBG_HELP_H
+
+#include "TSRM.h"
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_HELP(name) PHPDBG_COMMAND(help_##name)
+
+/**
+ * Helper Forward Declarations
+ */
+PHPDBG_HELP(aliases);
+
+extern const phpdbg_command_t phpdbg_help_commands[];
+
+#define phpdbg_help_header() \
+ phpdbg_notice("version", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
+#define phpdbg_help_footer() \
+ phpdbg_notice("issues", "url=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES);
+
+typedef struct _phpdbg_help_text_t {
+ char *key;
+ char *text;
+} phpdbg_help_text_t;
+
+extern phpdbg_help_text_t phpdbg_help_text[];
+#endif /* PHPDBG_HELP_H */
diff --git a/sapi/phpdbg/phpdbg_info.c b/sapi/phpdbg/phpdbg_info.c
new file mode 100644
index 0000000000..4774fa5a9e
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_info.c
@@ -0,0 +1,488 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "phpdbg.h"
+#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, flags) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14], flags)
+
+const phpdbg_command_t phpdbg_info_commands[] = {
+ PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(constants, "show user defined constants", 'd', info_constants, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_END_COMMAND
+};
+
+PHPDBG_INFO(break) /* {{{ */
+{
+ phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_SYM TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_FILE_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_FUNCTION_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD_OPLINE TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_COND TSRMLS_CC);
+ phpdbg_print_breakpoints(PHPDBG_BREAK_OPCODE TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(files) /* {{{ */
+{
+ HashPosition pos;
+ char *fname;
+
+ phpdbg_try_access {
+ phpdbg_notice("includedfilecount", "num=\"%d\"", "Included files: %d", zend_hash_num_elements(&EG(included_files)));
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Could not fetch included file count, invalid data source");
+ } phpdbg_end_try_access();
+
+ phpdbg_try_access {
+ zend_hash_internal_pointer_reset_ex(&EG(included_files), &pos);
+ while (zend_hash_get_current_key_ex(&EG(included_files), &fname, NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
+ phpdbg_writeln("includedfile", "name=\"%s\"", "File: %s", fname);
+ zend_hash_move_forward_ex(&EG(included_files), &pos);
+ }
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Could not fetch file name, invalid data source, aborting included file listing");
+ } phpdbg_end_try_access();
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(error) /* {{{ */
+{
+ if (PG(last_error_message)) {
+ phpdbg_try_access {
+ phpdbg_writeln("lasterror", "error=\"%s\" file=\"%s\" line=\"%d\"", "Last error: %s at %s line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
+ } phpdbg_catch_access {
+ phpdbg_notice("lasterror", "error=\"\"", "No error found!");
+ } phpdbg_end_try_access();
+ } else {
+ phpdbg_notice("lasterror", "error=\"\"", "No error found!");
+ }
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(constants) /* {{{ */
+{
+ HashPosition pos;
+ HashTable consts;
+ zend_constant *data;
+
+ zend_hash_init(&consts, 8, NULL, NULL, 0);
+
+ if (EG(zend_constants)) {
+ phpdbg_try_access {
+ zend_hash_internal_pointer_reset_ex(EG(zend_constants), &pos);
+ while (zend_hash_get_current_data_ex(EG(zend_constants), (void **) &data, &pos) == SUCCESS) {
+ if (data->module_number == PHP_USER_CONSTANT) {
+ zend_hash_update(&consts, data->name, data->name_len, (void **) &data, sizeof(zend_constant *), NULL);
+ }
+ zend_hash_move_forward_ex(EG(zend_constants), &pos);
+ }
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Cannot fetch all the constants, invalid data source");
+ } phpdbg_end_try_access();
+ }
+
+ phpdbg_notice("constantinfo", "num=\"%d\"", "User-defined constants (%d)", zend_hash_num_elements(&consts));
+
+ if (zend_hash_num_elements(&consts)) {
+ phpdbg_out("Address Refs Type Constant\n");
+ for (zend_hash_internal_pointer_reset_ex(&consts, &pos);
+ zend_hash_get_current_data_ex(&consts, (void **) &data, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(&consts, &pos)) {
+ data = *(zend_constant **) data;
+
+#define VARIABLEINFO(attrs, msg, ...) phpdbg_writeln("constant", "address=\"%p\" refcount=\"%d\" type=\"%s\" name=\"%.*s\" " attrs, "%-18p %-7d %-9s %.*s" msg, &data->value, Z_REFCOUNT(data->value), zend_zval_type_name(&data->value), data->name_len - 1, data->name, ##__VA_ARGS__)
+
+ switch (Z_TYPE(data->value)) {
+ case IS_STRING:
+ phpdbg_try_access {
+ VARIABLEINFO("length=\"%d\" value=\"%.*s\"", "\nstring (%d) \"%.*s%s\"", Z_STRLEN(data->value), Z_STRLEN(data->value) < 255 ? Z_STRLEN(data->value) : 255, Z_STRVAL(data->value), Z_STRLEN(data->value) > 255 ? "..." : "");
+ } phpdbg_catch_access {
+ VARIABLEINFO("", "");
+ } phpdbg_end_try_access();
+ break;
+ case IS_BOOL:
+ VARIABLEINFO("value=\"%s\"", "\nbool (%s)", Z_LVAL(data->value) ? "true" : "false");
+ break;
+ case IS_LONG:
+ VARIABLEINFO("value=\"%ld\"", "\nint (%ld)", Z_LVAL(data->value));
+ break;
+ case IS_DOUBLE:
+ VARIABLEINFO("value=\"%lf\"", "\ndouble (%lf)", Z_DVAL(data->value));
+ break;
+ default:
+ VARIABLEINFO("", "");
+
+#undef VARIABLEINFO
+ }
+ }
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static int phpdbg_arm_auto_global(zend_auto_global *auto_global TSRMLS_DC) {
+ if (auto_global->armed) {
+ if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
+ phpdbg_notice("variableinfo", "unreachable=\"%.*s\"", "Cannot show information about superglobal variable %.*s", auto_global->name_len, auto_global->name);
+ } else {
+ auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC);
+ }
+ }
+
+ return 0;
+}
+
+static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) {
+ HashTable vars, *symtable;
+ HashPosition pos;
+ char *var;
+ zval **data;
+
+ if (!EG(active_op_array)) {
+ phpdbg_error("inactive", "type=\"op_array\"", "No active op array!");
+ return SUCCESS;
+ }
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+
+ if (!EG(active_symbol_table)) {
+ phpdbg_error("inactive", "type=\"symbol_table\"", "No active symbol table!");
+ return SUCCESS;
+ }
+ }
+
+
+ if (show_globals) {
+ /* that array should only be manipulated during init, so safe for async access during execution */
+ zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_arm_auto_global TSRMLS_CC);
+ symtable = &EG(symbol_table);
+ } else {
+ symtable = EG(active_symbol_table);
+ }
+
+ zend_hash_init(&vars, 8, NULL, NULL, 0);
+
+ phpdbg_try_access {
+ zend_hash_internal_pointer_reset_ex(symtable, &pos);
+ while (zend_hash_get_current_key_ex(symtable, &var, NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
+ zend_hash_get_current_data_ex(symtable, (void **)&data, &pos);
+ if (zend_is_auto_global(var, strlen(var) TSRMLS_CC) ^ !show_globals) {
+ zend_hash_update(&vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL);
+ }
+ zend_hash_move_forward_ex(symtable, &pos);
+ }
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Cannot fetch all data from the symbol table, invalid data source");
+ } phpdbg_end_try_access();
+
+ if (show_globals) {
+ phpdbg_notice("variableinfo", "num=\"%d\"", "Superglobal variables (%d)", zend_hash_num_elements(&vars));
+ } else {
+ zend_op_array *ops = EG(active_op_array);
+
+ if (ops->function_name) {
+ if (ops->scope) {
+ phpdbg_notice("variableinfo", "method=\"%s::%s\" num=\"%d\"", "Variables in %s::%s() (%d)", ops->scope->name, ops->function_name, zend_hash_num_elements(&vars));
+ } else {
+ phpdbg_notice("variableinfo", "function=\"%s\" num=\"%d\"", "Variables in %s() (%d)", ops->function_name, zend_hash_num_elements(&vars));
+ }
+ } else {
+ if (ops->filename) {
+ phpdbg_notice("variableinfo", "file=\"%s\" num=\"%d\"", "Variables in %s (%d)", ops->filename, zend_hash_num_elements(&vars));
+ } else {
+ phpdbg_notice("variableinfo", "opline=\"%p\" num=\"%d\"", "Variables @ %p (%d)", ops, zend_hash_num_elements(&vars));
+ }
+ }
+ }
+
+ if (zend_hash_num_elements(&vars)) {
+ phpdbg_out("Address Refs Type Variable\n");
+ for (zend_hash_internal_pointer_reset_ex(&vars, &pos);
+ zend_hash_get_current_data_ex(&vars, (void**) &data, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(&vars, &pos)) {
+ char *var;
+ zend_bool invalid_data = 1;
+
+ zend_hash_get_current_key_ex(&vars, &var, NULL, NULL, 0, &pos);
+
+ phpdbg_try_access {
+ if (!(invalid_data = !*data)) {
+#define VARIABLEINFO(attrs, msg, ...) phpdbg_writeln("variable", "address=\"%p\" refcount=\"%d\" type=\"%s\" refstatus=\"%s\" name=\"%s\" " attrs, "%-18p %-7d %-9s %s$%s" msg, *data, Z_REFCOUNT_PP(data), zend_zval_type_name(*data), Z_ISREF_PP(data) ? "&": "", var, ##__VA_ARGS__)
+
+ switch (Z_TYPE_PP(data)) {
+ case IS_RESOURCE:
+ phpdbg_try_access {
+ int type;
+ VARIABLEINFO("type=\"%s\"", "\n|-------(typeof)------> (%s)\n", zend_list_find(Z_RESVAL_PP(data), &type) ? zend_rsrc_list_get_rsrc_type(type TSRMLS_CC) : "unknown");
+ } phpdbg_catch_access {
+ VARIABLEINFO("type=\"unknown\"", "\n|-------(typeof)------> (unknown)\n");
+ } phpdbg_end_try_access();
+ break;
+ case IS_OBJECT:
+ phpdbg_try_access {
+ VARIABLEINFO("instanceof=\"%s\"", "\n|-----(instanceof)----> (%s)\n", Z_OBJCE_PP(data)->name);
+ } phpdbg_catch_access {
+ VARIABLEINFO("instanceof=\"%s\"", "\n|-----(instanceof)----> (unknown)\n");
+ } phpdbg_end_try_access();
+ break;
+ case IS_STRING:
+ phpdbg_try_access {
+ VARIABLEINFO("length=\"%d\" value=\"%.*s\"", "\nstring (%d) \"%.*s%s\"", Z_STRLEN_PP(data), Z_STRLEN_PP(data) < 255 ? Z_STRLEN_PP(data) : 255, Z_STRVAL_PP(data), Z_STRLEN_PP(data) > 255 ? "..." : "");
+ } phpdbg_catch_access {
+ VARIABLEINFO("", "");
+ } phpdbg_end_try_access();
+ break;
+ case IS_BOOL:
+ VARIABLEINFO("value=\"%s\"", "\nbool (%s)", Z_LVAL_PP(data) ? "true" : "false");
+ break;
+ case IS_LONG:
+ VARIABLEINFO("value=\"%ld\"", "\nint (%ld)", Z_LVAL_PP(data));
+ break;
+ case IS_DOUBLE:
+ VARIABLEINFO("value=\"%lf\"", "\ndouble (%lf)", Z_DVAL_PP(data));
+ break;
+ default:
+ VARIABLEINFO("", "");
+ }
+ }
+
+#undef VARIABLEINFO
+ } phpdbg_end_try_access();
+
+ if (invalid_data) {
+ phpdbg_writeln("variable", "name=\"%s\"", "n/a\tn/a\tn/a\t$%s", var);
+ }
+ }
+ }
+
+ zend_hash_destroy(&vars);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(vars) /* {{{ */
+{
+ return phpdbg_print_symbols(0 TSRMLS_CC);
+}
+
+PHPDBG_INFO(globals) /* {{{ */
+{
+ return phpdbg_print_symbols(1 TSRMLS_CC);
+}
+
+PHPDBG_INFO(literal) /* {{{ */
+{
+ /* literals are assumed to not be manipulated during executing of their op_array and as such async safe */
+ if ((EG(in_execution) && EG(active_op_array)) || PHPDBG_G(ops)) {
+ zend_op_array *ops = EG(active_op_array) ? EG(active_op_array) : PHPDBG_G(ops);
+ int literal = 0, count = ops->last_literal-1;
+
+ if (ops->function_name) {
+ if (ops->scope) {
+ phpdbg_notice("literalinfo", "method=\"%s::%s\" num=\"%d\"", "Literal Constants in %s::%s() (%d)", ops->scope->name, ops->function_name, count);
+ } else {
+ phpdbg_notice("literalinfo", "function=\"%s\" num=\"%d\"", "Literal Constants in %s() (%d)", ops->function_name, count);
+ }
+ } else {
+ if (ops->filename) {
+ phpdbg_notice("literalinfo", "file=\"%s\" num=\"%d\"", "Literal Constants in %s (%d)", ops->filename, count);
+ } else {
+ phpdbg_notice("literalinfo", "opline=\"%p\" num=\"%d\"", "Literal Constants @ %p (%d)", ops, count);
+ }
+ }
+
+ while (literal < ops->last_literal) {
+ if (Z_TYPE(ops->literals[literal].constant) != IS_NULL) {
+ phpdbg_write("literal", "id=\"%u\"", "|-------- C%u -------> [", literal);
+ zend_print_zval(&ops->literals[literal].constant, 0);
+ phpdbg_out("]\n");
+ }
+ literal++;
+ }
+ } else {
+ phpdbg_error("inactive", "type=\"execution\"", "Not executing!");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(memory) /* {{{ */
+{
+ size_t used, real, peak_used, peak_real;
+ zend_mm_heap *heap;
+ zend_bool is_mm;
+
+ if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
+ heap = zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem(TSRMLS_C) TSRMLS_CC);
+ }
+ if ((is_mm = is_zend_mm(TSRMLS_C))) {
+ used = zend_memory_usage(0 TSRMLS_CC);
+ real = zend_memory_usage(1 TSRMLS_CC);
+ peak_used = zend_memory_peak_usage(0 TSRMLS_CC);
+ peak_real = zend_memory_peak_usage(1 TSRMLS_CC);
+ }
+ if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
+ zend_mm_set_heap(heap TSRMLS_CC);
+ }
+
+ if (is_mm) {
+ phpdbg_notice("meminfo", "", "Memory Manager Information");
+ phpdbg_notice("current", "", "Current");
+ phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (used / 1024));
+ phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (real / 1024));
+ phpdbg_notice("peak", "", "Peak");
+ phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (peak_used / 1024));
+ phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (peak_real / 1024));
+ } else {
+ phpdbg_error("inactive", "type=\"memory_manager\"", "Memory Manager Disabled!");
+ }
+ return SUCCESS;
+} /* }}} */
+
+static inline void phpdbg_print_class_name(zend_class_entry **ce TSRMLS_DC) /* {{{ */
+{
+ phpdbg_writeln("class", "type=\"%s\" flags=\"%s\" name=\"%s\" methodcount=\"%d\"", "%s %s %s (%d)",
+ ((*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, zend_hash_num_elements(&(*ce)->function_table));
+} /* }}} */
+
+PHPDBG_INFO(classes) /* {{{ */
+{
+ HashPosition position;
+ zend_class_entry **ce;
+ HashTable classes;
+
+ zend_hash_init(&classes, 8, NULL, NULL, 0);
+
+ phpdbg_try_access {
+ for (zend_hash_internal_pointer_reset_ex(EG(class_table), &position);
+ zend_hash_get_current_data_ex(EG(class_table), (void**)&ce, &position) == SUCCESS;
+ zend_hash_move_forward_ex(EG(class_table), &position)) {
+ if ((*ce)->type == ZEND_USER_CLASS) {
+ zend_hash_next_index_insert(&classes, ce, sizeof(ce), NULL);
+ }
+ }
+ } phpdbg_catch_access {
+ phpdbg_notice("signalsegv", "", "Not all classes could be fetched, possibly invalid data source");
+ } phpdbg_end_try_access();
+
+ phpdbg_notice("classinfo", "num=\"%d\"", "User Classes (%d)", zend_hash_num_elements(&classes));
+
+ /* once added, assume that classes are stable... until shutdown. */
+ for (zend_hash_internal_pointer_reset_ex(&classes, &position);
+ zend_hash_get_current_data_ex(&classes, (void**)&ce, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&classes, &position)) {
+
+ phpdbg_print_class_name(ce TSRMLS_CC);
+
+ if ((*ce)->parent) {
+ zend_class_entry *pce;
+
+ phpdbg_xml("<parents %r>");
+ pce = (*ce)->parent;
+ do {
+ phpdbg_out("|-------- ");
+ phpdbg_print_class_name(&pce TSRMLS_CC);
+ } while ((pce = pce->parent));
+ phpdbg_xml("</parents>");
+ }
+
+ if ((*ce)->info.user.filename) {
+ phpdbg_writeln("classsource", "file=\"%s\" line=\"%u\"", "|---- in %s on line %u", (*ce)->info.user.filename, (*ce)->info.user.line_start);
+ } else {
+ phpdbg_writeln("classsource", "", "|---- no source code");
+ }
+ }
+
+ zend_hash_destroy(&classes);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_INFO(funcs) /* {{{ */
+{
+ HashPosition position;
+ zend_function *zf, **pzf;
+ HashTable functions;
+
+ zend_hash_init(&functions, 8, NULL, NULL, 0);
+
+ phpdbg_try_access {
+ for (zend_hash_internal_pointer_reset_ex(EG(function_table), &position);
+ zend_hash_get_current_data_ex(EG(function_table), (void**)&zf, &position) == SUCCESS;
+ zend_hash_move_forward_ex(EG(function_table), &position)) {
+ if (zf->type == ZEND_USER_FUNCTION) {
+ zend_hash_next_index_insert(&functions, (void**) &zf, sizeof(zend_function), NULL);
+ }
+ }
+ } phpdbg_catch_access {
+ phpdbg_notice("signalsegv", "", "Not all functions could be fetched, possibly invalid data source");
+ } phpdbg_end_try_access();
+
+ phpdbg_notice("functioninfo", "num=\"%d\"", "User Functions (%d)", zend_hash_num_elements(&functions));
+
+ for (zend_hash_internal_pointer_reset_ex(&functions, &position);
+ zend_hash_get_current_data_ex(&functions, (void**)&pzf, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&functions, &position)) {
+ zend_op_array *op_array = &((*pzf)->op_array);
+
+ phpdbg_write("function", "name=\"%s\"", "|-------- %s", op_array->function_name ? op_array->function_name : "{main}");
+
+ if (op_array->filename) {
+ phpdbg_writeln("functionsource", "file=\"%s\" line=\"%d\"", " in %s on line %d",
+ op_array->filename,
+ op_array->line_start);
+ } else {
+ phpdbg_writeln("functionsource", "", " (no source code)");
+ }
+ }
+
+ zend_hash_destroy(&functions);
+
+ return SUCCESS;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_info.h b/sapi/phpdbg/phpdbg_info.h
new file mode 100644
index 0000000000..aac9fa3ab3
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_info.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_INFO_H
+#define PHPDBG_INFO_H
+
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_INFO(name) PHPDBG_COMMAND(info_##name)
+
+PHPDBG_INFO(files);
+PHPDBG_INFO(break);
+PHPDBG_INFO(classes);
+PHPDBG_INFO(funcs);
+PHPDBG_INFO(error);
+PHPDBG_INFO(constants);
+PHPDBG_INFO(vars);
+PHPDBG_INFO(globals);
+PHPDBG_INFO(literal);
+PHPDBG_INFO(memory);
+
+extern const phpdbg_command_t phpdbg_info_commands[];
+
+#endif /* PHPDBG_INFO_H */
diff --git a/sapi/phpdbg/phpdbg_io.c b/sapi/phpdbg/phpdbg_io.c
new file mode 100644
index 0000000000..97f0356285
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_io.c
@@ -0,0 +1,272 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "phpdbg_io.h"
+
+#ifdef PHP_WIN32
+#undef UNICODE
+#include "win32/inet.h"
+#include <winsock2.h>
+#include <windows.h>
+#include <Ws2tcpip.h>
+#include "win32/sockets.h"
+
+#else
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+#include <poll.h>
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_API int phpdbg_consume_bytes(int sock, char *ptr, int len, int tmo TSRMLS_DC) {
+ int got_now, i = len, j;
+ char *p = ptr;
+#ifndef PHP_WIN32
+ struct pollfd pfd;
+
+ if (tmo < 0) goto recv_once;
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+
+ j = poll(&pfd, 1, tmo);
+
+ if (j == 0) {
+#else
+ struct fd_set readfds;
+ struct timeval ttmo;
+
+ if (tmo < 0) goto recv_once;
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+
+ ttmo.tv_sec = 0;
+ ttmo.tv_usec = tmo*1000;
+
+ j = select(0, &readfds, NULL, NULL, &ttmo);
+
+ if (j <= 0) {
+#endif
+ return -1;
+ }
+
+recv_once:
+ while(i > 0) {
+ if (tmo < 0) {
+ /* There's something to read. Read what's available and proceed
+ disregarding whether len could be exhausted or not.*/
+ int can_read = recv(sock, p, i, MSG_PEEK);
+#ifndef _WIN32
+ if (can_read == -1 && errno == EINTR) {
+ continue;
+ }
+#endif
+ i = can_read;
+ }
+
+#ifdef _WIN32
+ got_now = recv(sock, p, i, 0);
+#else
+ do {
+ got_now = recv(sock, p, i, 0);
+ } while (got_now == -1 && errno == EINTR);
+#endif
+
+ if (got_now == -1) {
+ write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Read operation timed out!\n"));
+ return -1;
+ }
+ i -= got_now;
+ p += got_now;
+ }
+
+ return p - ptr;
+}
+
+PHPDBG_API int phpdbg_send_bytes(int sock, const char *ptr, int len) {
+ int sent, i = len;
+ const char *p = ptr;
+/* XXX poll/select needed here? */
+ while(i > 0) {
+ sent = send(sock, p, i, 0);
+ if (sent == -1) {
+ return -1;
+ }
+ i -= sent;
+ p += sent;
+ }
+
+ return len;
+}
+
+
+PHPDBG_API int phpdbg_mixed_read(int sock, char *ptr, int len, int tmo TSRMLS_DC) {
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
+ return phpdbg_consume_bytes(sock, ptr, len, tmo TSRMLS_CC);
+ }
+
+ return read(sock, ptr, len);
+}
+
+
+PHPDBG_API int phpdbg_mixed_write(int sock, const char *ptr, int len TSRMLS_DC) {
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
+ return phpdbg_send_bytes(sock, ptr, len);
+ }
+
+ return write(sock, ptr, len);
+}
+
+
+PHPDBG_API int phpdbg_open_socket(const char *interface, unsigned short port TSRMLS_DC) {
+ struct addrinfo res;
+ int fd = phpdbg_create_listenable_socket(interface, port, &res TSRMLS_CC);
+
+ if (fd == -1) {
+ return -1;
+ }
+
+ if (bind(fd, res.ai_addr, res.ai_addrlen) == -1) {
+ phpdbg_close_socket(fd);
+ return -4;
+ }
+
+ listen(fd, 5);
+
+ return fd;
+}
+
+
+PHPDBG_API int phpdbg_create_listenable_socket(const char *addr, unsigned short port, struct addrinfo *addr_res TSRMLS_DC) {
+ int sock = -1, rc;
+ int reuse = 1;
+ struct in6_addr serveraddr;
+ struct addrinfo hints, *res = NULL;
+ char port_buf[8];
+ int8_t any_addr = *addr == '*';
+
+ do {
+ memset(&hints, 0, sizeof hints);
+ if (any_addr) {
+ hints.ai_flags = AI_PASSIVE;
+ } else {
+ hints.ai_flags = AI_NUMERICSERV;
+ }
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ rc = inet_pton(AF_INET, addr, &serveraddr);
+ if (1 == rc) {
+ hints.ai_family = AF_INET;
+ if (!any_addr) {
+ hints.ai_flags |= AI_NUMERICHOST;
+ }
+ } else {
+ rc = inet_pton(AF_INET6, addr, &serveraddr);
+ if (1 == rc) {
+ hints.ai_family = AF_INET6;
+ if (!any_addr) {
+ hints.ai_flags |= AI_NUMERICHOST;
+ }
+ } else {
+ /* XXX get host by name ??? */
+ }
+ }
+
+ snprintf(port_buf, 7, "%u", port);
+ if (!any_addr) {
+ rc = getaddrinfo(addr, port_buf, &hints, &res);
+ } else {
+ rc = getaddrinfo(NULL, port_buf, &hints, &res);
+ }
+
+ if (0 != rc) {
+#ifndef PHP_WIN32
+ if (rc == EAI_SYSTEM) {
+ char buf[128];
+ int wrote;
+
+ wrote = snprintf(buf, 128, "Could not translate address '%s'", addr);
+ buf[wrote] = '\0';
+ write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf));
+
+ return sock;
+ } else {
+#endif
+ char buf[256];
+ int wrote;
+
+ wrote = snprintf(buf, 256, "Host '%s' not found. %s", addr, estrdup(gai_strerror(rc)));
+ buf[wrote] = '\0';
+ write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf));
+
+ return sock;
+#ifndef PHP_WIN32
+ }
+#endif
+ return sock;
+ }
+
+ if((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
+ char buf[128];
+ int wrote;
+
+ wrote = sprintf(buf, "Unable to create socket");
+ buf[wrote] = '\0';
+ write(PHPDBG_G(io)[PHPDBG_STDERR].fd, buf, strlen(buf));
+
+ return sock;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse)) == -1) {
+ phpdbg_close_socket(sock);
+ return sock;
+ }
+
+
+ } while (0);
+
+ *addr_res = *res;
+
+ return sock;
+}
+
+PHPDBG_API void phpdbg_close_socket(int sock) {
+ if (socket >= 0) {
+#ifdef _WIN32
+ closesocket(sock);
+#else
+ shutdown(sock, SHUT_RDWR);
+ close(sock);
+#endif
+ }
+}
+
diff --git a/sapi/phpdbg/phpdbg_io.h b/sapi/phpdbg/phpdbg_io.h
new file mode 100644
index 0000000000..3ac8f4112d
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_io.h
@@ -0,0 +1,34 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_IO_H
+#define PHPDBG_IO_H
+
+#include "phpdbg.h"
+
+PHPDBG_API int phpdbg_consume_bytes(int sock, char *ptr, int len, int tmo TSRMLS_DC);
+PHPDBG_API int phpdbg_send_bytes(int sock, const char *ptr, int len);
+PHPDBG_API int phpdbg_mixed_read(int sock, char *ptr, int len, int tmo TSRMLS_DC);
+PHPDBG_API int phpdbg_mixed_write(int sock, const char *ptr, int len TSRMLS_DC);
+
+PHPDBG_API int phpdbg_create_listenable_socket(const char *addr, unsigned short port, struct addrinfo *res TSRMLS_DC);
+PHPDBG_API int phpdbg_open_socket(const char *interface, unsigned short port TSRMLS_DC);
+PHPDBG_API void phpdbg_close_socket(int sock);
+
+#endif /* PHPDBG_IO_H */
+
diff --git a/sapi/phpdbg/phpdbg_lexer.c b/sapi/phpdbg/phpdbg_lexer.c
new file mode 100644
index 0000000000..90f3a449da
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.c
@@ -0,0 +1,1519 @@
+/* Generated by re2c 0.13.5 */
+#line 1 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+/*
+ * phpdbg_lexer.l
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+
+#include "phpdbg_parser.h"
+
+#define LEX(v) (PHPDBG_G(lexer).v)
+
+#define YYCTYPE unsigned char
+#define YYSETCONDITION(x) LEX(state) = x;
+#define YYGETCONDITION() LEX(state)
+#define YYCURSOR LEX(cursor)
+#define YYMARKER LEX(marker)
+#define yyleng LEX(len)
+#define yytext ((char*) LEX(text))
+#undef YYDEBUG
+#define YYDEBUG(a, b)
+#define YYFILL(n)
+
+#define NORMAL 0
+#define PRE_RAW 1
+#define RAW 2
+#define INITIAL 3
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+void phpdbg_init_lexer (phpdbg_param_t *stack, char *input TSRMLS_DC) {
+ PHPDBG_G(parser_stack) = stack;
+
+ YYSETCONDITION(INITIAL);
+
+ LEX(text) = YYCURSOR = (unsigned char *) input;
+ LEX(len) = strlen(input);
+}
+
+int phpdbg_lex (phpdbg_param_t* yylval) {
+ TSRMLS_FETCH(); /* Slow, but this is not a major problem here. TODO: Use TSRMLS_DC */
+
+restart:
+ LEX(text) = YYCURSOR;
+
+
+#line 49 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+{
+ YYCTYPE yych;
+ unsigned int yyaccept = 0;
+ if (YYGETCONDITION() < 2) {
+ if (YYGETCONDITION() < 1) {
+ goto yyc_NORMAL;
+ } else {
+ goto yyc_PRE_RAW;
+ }
+ } else {
+ if (YYGETCONDITION() < 3) {
+ goto yyc_RAW;
+ } else {
+ goto yyc_INITIAL;
+ }
+ }
+/* *********************************** */
+yyc_INITIAL:
+ {
+ static const unsigned char yybm[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 192, 96, 0, 0, 192, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+ YYDEBUG(0, *YYCURSOR);
+ YYFILL(4);
+ yych = *YYCURSOR;
+ if (yybm[0+yych] & 32) {
+ goto yy4;
+ }
+ if (yych <= 'E') {
+ if (yych <= '\f') {
+ if (yych <= 0x00) goto yy7;
+ if (yych != '\t') goto yy12;
+ } else {
+ if (yych <= 0x1F) {
+ if (yych >= 0x0E) goto yy12;
+ } else {
+ if (yych <= ' ') goto yy2;
+ if (yych <= 'D') goto yy12;
+ goto yy8;
+ }
+ }
+ } else {
+ if (yych <= 'd') {
+ if (yych <= 'Q') goto yy12;
+ if (yych <= 'R') goto yy11;
+ if (yych <= 'S') goto yy9;
+ goto yy12;
+ } else {
+ if (yych <= 'q') {
+ if (yych <= 'e') goto yy8;
+ goto yy12;
+ } else {
+ if (yych <= 'r') goto yy10;
+ if (yych <= 's') goto yy9;
+ goto yy12;
+ }
+ }
+ }
+yy2:
+ YYDEBUG(2, *YYCURSOR);
+ ++YYCURSOR;
+ if ((yych = *YYCURSOR) <= '\f') {
+ if (yych <= 0x00) goto yy29;
+ if (yych <= 0x08) goto yy3;
+ if (yych <= '\n') goto yy29;
+ } else {
+ if (yych <= '\r') goto yy29;
+ if (yych == ' ') goto yy29;
+ }
+yy3:
+ YYDEBUG(3, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 176 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ YYSETCONDITION(NORMAL);
+
+ YYCURSOR = LEX(text);
+ goto restart;
+}
+#line 161 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy4:
+ YYDEBUG(4, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(5, *YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy28;
+ }
+ if (yych <= 0x00) goto yy27;
+ if (yych == '\n') goto yy4;
+yy6:
+ YYDEBUG(6, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 69 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return 0;
+}
+#line 180 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy7:
+ YYDEBUG(7, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy3;
+yy8:
+ YYDEBUG(8, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'V') goto yy23;
+ if (yych == 'v') goto yy23;
+ goto yy3;
+yy9:
+ YYDEBUG(9, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'H') goto yy19;
+ if (yych == 'h') goto yy19;
+ goto yy3;
+yy10:
+ YYDEBUG(10, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy16;
+ }
+ if (yych == 'U') goto yy13;
+ if (yych == 'u') goto yy13;
+ goto yy3;
+yy11:
+ YYDEBUG(11, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'U') goto yy13;
+ if (yych == 'u') goto yy13;
+ goto yy3;
+yy12:
+ YYDEBUG(12, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy3;
+yy13:
+ YYDEBUG(13, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yych == 'N') goto yy15;
+ if (yych == 'n') goto yy15;
+yy14:
+ YYDEBUG(14, *YYCURSOR);
+ YYCURSOR = YYMARKER;
+ goto yy3;
+yy15:
+ YYDEBUG(15, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yybm[0+yych] & 64) {
+ goto yy16;
+ }
+ goto yy14;
+yy16:
+ YYDEBUG(16, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(17, *YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy16;
+ }
+ YYDEBUG(18, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 163 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ YYSETCONDITION(PRE_RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_RUN;
+}
+#line 253 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy19:
+ YYDEBUG(19, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy14;
+ if (yych >= '\v') goto yy14;
+ } else {
+ if (yych <= '\r') goto yy20;
+ if (yych != ' ') goto yy14;
+ }
+yy20:
+ YYDEBUG(20, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(21, *YYCURSOR);
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy22;
+ if (yych <= '\n') goto yy20;
+ } else {
+ if (yych <= '\r') goto yy20;
+ if (yych == ' ') goto yy20;
+ }
+yy22:
+ YYDEBUG(22, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 158 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ YYSETCONDITION(PRE_RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_SHELL;
+}
+#line 286 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy23:
+ YYDEBUG(23, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy14;
+ if (yych >= '\v') goto yy14;
+ } else {
+ if (yych <= '\r') goto yy24;
+ if (yych != ' ') goto yy14;
+ }
+yy24:
+ YYDEBUG(24, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(25, *YYCURSOR);
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy26;
+ if (yych <= '\n') goto yy24;
+ } else {
+ if (yych <= '\r') goto yy24;
+ if (yych == ' ') goto yy24;
+ }
+yy26:
+ YYDEBUG(26, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 153 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ YYSETCONDITION(PRE_RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_EVAL;
+}
+#line 319 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy27:
+ YYDEBUG(27, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy6;
+yy28:
+ YYDEBUG(28, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+yy29:
+ YYDEBUG(29, *YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy28;
+ }
+ if (yych <= 0x00) goto yy27;
+ if (yych == '\n') goto yy4;
+ YYDEBUG(30, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 147 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ /* ignore whitespace */
+
+ goto restart;
+}
+#line 344 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+ }
+/* *********************************** */
+yyc_NORMAL:
+ {
+ static const unsigned char yybm[] = {
+ 0, 8, 8, 8, 8, 8, 8, 8,
+ 8, 66, 68, 8, 8, 66, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 66, 8, 8, 0, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 24, 8,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 0, 8, 8, 8, 8, 8,
+ 8, 168, 168, 168, 168, 168, 168, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 8, 8, 8, 8, 8,
+ 8, 168, 168, 168, 168, 168, 168, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ };
+ YYDEBUG(31, *YYCURSOR);
+ YYFILL(11);
+ yych = *YYCURSOR;
+ if (yybm[0+yych] & 2) {
+ goto yy33;
+ }
+ if (yych <= 'N') {
+ if (yych <= '0') {
+ if (yych <= '#') {
+ if (yych <= '\t') {
+ if (yych <= 0x00) goto yy39;
+ goto yy43;
+ } else {
+ if (yych <= '\n') goto yy36;
+ if (yych <= '"') goto yy43;
+ goto yy58;
+ }
+ } else {
+ if (yych <= '-') {
+ if (yych <= ',') goto yy43;
+ goto yy40;
+ } else {
+ if (yych <= '.') goto yy45;
+ if (yych <= '/') goto yy43;
+ goto yy48;
+ }
+ }
+ } else {
+ if (yych <= 'E') {
+ if (yych <= ':') {
+ if (yych <= '9') goto yy45;
+ goto yy60;
+ } else {
+ if (yych <= 'C') goto yy43;
+ if (yych <= 'D') goto yy49;
+ goto yy50;
+ }
+ } else {
+ if (yych <= 'H') {
+ if (yych <= 'F') goto yy51;
+ goto yy43;
+ } else {
+ if (yych <= 'I') goto yy42;
+ if (yych <= 'M') goto yy43;
+ goto yy52;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'f') {
+ if (yych <= 'Y') {
+ if (yych <= 'S') {
+ if (yych <= 'O') goto yy53;
+ goto yy43;
+ } else {
+ if (yych <= 'T') goto yy54;
+ if (yych <= 'X') goto yy43;
+ goto yy55;
+ }
+ } else {
+ if (yych <= 'c') {
+ if (yych <= 'Z') goto yy56;
+ goto yy43;
+ } else {
+ if (yych <= 'd') goto yy49;
+ if (yych <= 'e') goto yy50;
+ goto yy51;
+ }
+ }
+ } else {
+ if (yych <= 'o') {
+ if (yych <= 'i') {
+ if (yych <= 'h') goto yy43;
+ goto yy42;
+ } else {
+ if (yych <= 'm') goto yy43;
+ if (yych <= 'n') goto yy52;
+ goto yy53;
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych == 't') goto yy54;
+ goto yy43;
+ } else {
+ if (yych <= 'y') goto yy55;
+ if (yych <= 'z') goto yy57;
+ goto yy43;
+ }
+ }
+ }
+ }
+yy33:
+ YYDEBUG(33, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(34, *YYCURSOR);
+ if (yybm[0+yych] & 2) {
+ goto yy33;
+ }
+ if (yych <= 0x00) goto yy39;
+ if (yych == '\n') goto yy36;
+ YYDEBUG(35, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 147 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ /* ignore whitespace */
+
+ goto restart;
+}
+#line 493 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy36:
+ YYDEBUG(36, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(37, *YYCURSOR);
+ if (yybm[0+yych] & 2) {
+ goto yy33;
+ }
+ if (yych <= 0x00) goto yy39;
+ if (yych == '\n') goto yy36;
+yy38:
+ YYDEBUG(38, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 69 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return 0;
+}
+#line 512 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy39:
+ YYDEBUG(39, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy38;
+yy40:
+ YYDEBUG(40, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yybm[0+yych] & 16) {
+ goto yy45;
+ }
+ if (yych == 'r') goto yy113;
+ goto yy44;
+yy41:
+ YYDEBUG(41, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 133 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_ID;
+}
+#line 536 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy42:
+ YYDEBUG(42, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'F') goto yy109;
+ if (yych == 'f') goto yy109;
+ goto yy44;
+yy43:
+ YYDEBUG(43, *YYCURSOR);
+ yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ YYFILL(3);
+ yych = *YYCURSOR;
+yy44:
+ YYDEBUG(44, *YYCURSOR);
+ if (yybm[0+yych] & 8) {
+ goto yy43;
+ }
+ if (yych <= '9') goto yy41;
+ goto yy65;
+yy45:
+ YYDEBUG(45, *YYCURSOR);
+ yyaccept = 1;
+ YYMARKER = ++YYCURSOR;
+ YYFILL(3);
+ yych = *YYCURSOR;
+ YYDEBUG(46, *YYCURSOR);
+ if (yybm[0+yych] & 16) {
+ goto yy45;
+ }
+ if (yych <= 0x1F) {
+ if (yych <= '\n') {
+ if (yych <= 0x00) goto yy47;
+ if (yych <= 0x08) goto yy43;
+ } else {
+ if (yych != '\r') goto yy43;
+ }
+ } else {
+ if (yych <= '#') {
+ if (yych <= ' ') goto yy47;
+ if (yych <= '"') goto yy43;
+ } else {
+ if (yych == ':') goto yy65;
+ goto yy43;
+ }
+ }
+yy47:
+ YYDEBUG(47, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 114 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = atoi(yytext);
+ return T_DIGITS;
+}
+#line 592 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy48:
+ YYDEBUG(48, *YYCURSOR);
+ yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yybm[0+yych] & 16) {
+ goto yy45;
+ }
+ if (yych <= 0x1F) {
+ if (yych <= '\n') {
+ if (yych <= 0x00) goto yy47;
+ if (yych <= 0x08) goto yy44;
+ goto yy47;
+ } else {
+ if (yych == '\r') goto yy47;
+ goto yy44;
+ }
+ } else {
+ if (yych <= '#') {
+ if (yych <= ' ') goto yy47;
+ if (yych <= '"') goto yy44;
+ goto yy47;
+ } else {
+ if (yych == 'x') goto yy105;
+ goto yy44;
+ }
+ }
+yy49:
+ YYDEBUG(49, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'I') goto yy99;
+ if (yych == 'i') goto yy99;
+ goto yy44;
+yy50:
+ YYDEBUG(50, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'N') goto yy94;
+ if (yych == 'n') goto yy94;
+ goto yy44;
+yy51:
+ YYDEBUG(51, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'A') goto yy91;
+ if (yych == 'a') goto yy91;
+ goto yy44;
+yy52:
+ YYDEBUG(52, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'O') goto yy87;
+ if (yych == 'o') goto yy87;
+ goto yy44;
+yy53:
+ YYDEBUG(53, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 'N') {
+ if (yych == 'F') goto yy86;
+ if (yych <= 'M') goto yy44;
+ goto yy80;
+ } else {
+ if (yych <= 'f') {
+ if (yych <= 'e') goto yy44;
+ goto yy86;
+ } else {
+ if (yych == 'n') goto yy80;
+ goto yy44;
+ }
+ }
+yy54:
+ YYDEBUG(54, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'R') goto yy84;
+ if (yych == 'r') goto yy84;
+ goto yy44;
+yy55:
+ YYDEBUG(55, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy79;
+ if (yych == 'e') goto yy79;
+ goto yy44;
+yy56:
+ YYDEBUG(56, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy76;
+ goto yy44;
+yy57:
+ YYDEBUG(57, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'e') goto yy64;
+ goto yy44;
+yy58:
+ YYDEBUG(58, *YYCURSOR);
+ ++YYCURSOR;
+ YYDEBUG(59, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 92 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return T_POUND;
+}
+#line 699 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy60:
+ YYDEBUG(60, *YYCURSOR);
+ ++YYCURSOR;
+ if ((yych = *YYCURSOR) == ':') goto yy62;
+ YYDEBUG(61, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 98 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return T_COLON;
+}
+#line 710 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy62:
+ YYDEBUG(62, *YYCURSOR);
+ ++YYCURSOR;
+ YYDEBUG(63, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 95 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return T_DCOLON;
+}
+#line 720 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy64:
+ YYDEBUG(64, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'n') goto yy70;
+ goto yy44;
+yy65:
+ YYDEBUG(65, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yych == '/') goto yy67;
+yy66:
+ YYDEBUG(66, *YYCURSOR);
+ YYCURSOR = YYMARKER;
+ if (yyaccept <= 2) {
+ if (yyaccept <= 1) {
+ if (yyaccept <= 0) {
+ goto yy41;
+ } else {
+ goto yy47;
+ }
+ } else {
+ goto yy75;
+ }
+ } else {
+ if (yyaccept <= 3) {
+ goto yy108;
+ } else {
+ goto yy119;
+ }
+ }
+yy67:
+ YYDEBUG(67, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yych != '/') goto yy66;
+ YYDEBUG(68, *YYCURSOR);
+ ++YYCURSOR;
+ YYDEBUG(69, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 86 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_PROTO;
+}
+#line 766 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy70:
+ YYDEBUG(70, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych != 'd') goto yy44;
+ YYDEBUG(71, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych != '_') goto yy44;
+yy72:
+ YYDEBUG(72, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yybm[0+yych] & 32) {
+ goto yy73;
+ }
+ goto yy44;
+yy73:
+ YYDEBUG(73, *YYCURSOR);
+ yyaccept = 2;
+ YYMARKER = ++YYCURSOR;
+ YYFILL(3);
+ yych = *YYCURSOR;
+ YYDEBUG(74, *YYCURSOR);
+ if (yybm[0+yych] & 32) {
+ goto yy73;
+ }
+ if (yych <= 0x1F) {
+ if (yych <= '\n') {
+ if (yych <= 0x00) goto yy75;
+ if (yych <= 0x08) goto yy43;
+ } else {
+ if (yych != '\r') goto yy43;
+ }
+ } else {
+ if (yych <= '#') {
+ if (yych <= ' ') goto yy75;
+ if (yych <= '"') goto yy43;
+ } else {
+ if (yych == ':') goto yy65;
+ goto yy43;
+ }
+ }
+yy75:
+ YYDEBUG(75, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 126 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, OP_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_OPCODE;
+}
+#line 820 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy76:
+ YYDEBUG(76, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych != 'N') goto yy44;
+ YYDEBUG(77, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych != 'D') goto yy44;
+ YYDEBUG(78, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == '_') goto yy72;
+ goto yy44;
+yy79:
+ YYDEBUG(79, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'S') goto yy80;
+ if (yych != 's') goto yy44;
+yy80:
+ YYDEBUG(80, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy81;
+ }
+ goto yy44;
+yy81:
+ YYDEBUG(81, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(82, *YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy81;
+ }
+ YYDEBUG(83, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 102 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 1;
+ return T_TRUTHY;
+}
+#line 866 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy84:
+ YYDEBUG(84, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'U') goto yy85;
+ if (yych != 'u') goto yy44;
+yy85:
+ YYDEBUG(85, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy80;
+ if (yych == 'e') goto yy80;
+ goto yy44;
+yy86:
+ YYDEBUG(86, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'F') goto yy87;
+ if (yych != 'f') goto yy44;
+yy87:
+ YYDEBUG(87, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy44;
+ if (yych >= '\v') goto yy44;
+ } else {
+ if (yych <= '\r') goto yy88;
+ if (yych != ' ') goto yy44;
+ }
+yy88:
+ YYDEBUG(88, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(89, *YYCURSOR);
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy90;
+ if (yych <= '\n') goto yy88;
+ } else {
+ if (yych <= '\r') goto yy88;
+ if (yych == ' ') goto yy88;
+ }
+yy90:
+ YYDEBUG(90, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 108 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 0;
+ return T_FALSY;
+}
+#line 919 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy91:
+ YYDEBUG(91, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'L') goto yy92;
+ if (yych != 'l') goto yy44;
+yy92:
+ YYDEBUG(92, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'S') goto yy93;
+ if (yych != 's') goto yy44;
+yy93:
+ YYDEBUG(93, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy87;
+ if (yych == 'e') goto yy87;
+ goto yy44;
+yy94:
+ YYDEBUG(94, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'A') goto yy95;
+ if (yych != 'a') goto yy44;
+yy95:
+ YYDEBUG(95, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'B') goto yy96;
+ if (yych != 'b') goto yy44;
+yy96:
+ YYDEBUG(96, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'L') goto yy97;
+ if (yych != 'l') goto yy44;
+yy97:
+ YYDEBUG(97, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy98;
+ if (yych != 'e') goto yy44;
+yy98:
+ YYDEBUG(98, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'D') goto yy80;
+ if (yych == 'd') goto yy80;
+ goto yy44;
+yy99:
+ YYDEBUG(99, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'S') goto yy100;
+ if (yych != 's') goto yy44;
+yy100:
+ YYDEBUG(100, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'A') goto yy101;
+ if (yych != 'a') goto yy44;
+yy101:
+ YYDEBUG(101, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'B') goto yy102;
+ if (yych != 'b') goto yy44;
+yy102:
+ YYDEBUG(102, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'L') goto yy103;
+ if (yych != 'l') goto yy44;
+yy103:
+ YYDEBUG(103, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'E') goto yy104;
+ if (yych != 'e') goto yy44;
+yy104:
+ YYDEBUG(104, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'D') goto yy87;
+ if (yych == 'd') goto yy87;
+ goto yy44;
+yy105:
+ YYDEBUG(105, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy106;
+ }
+ goto yy44;
+yy106:
+ YYDEBUG(106, *YYCURSOR);
+ yyaccept = 3;
+ YYMARKER = ++YYCURSOR;
+ YYFILL(3);
+ yych = *YYCURSOR;
+ YYDEBUG(107, *YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy106;
+ }
+ if (yych <= 0x1F) {
+ if (yych <= '\n') {
+ if (yych <= 0x00) goto yy108;
+ if (yych <= 0x08) goto yy43;
+ } else {
+ if (yych != '\r') goto yy43;
+ }
+ } else {
+ if (yych <= '#') {
+ if (yych <= ' ') goto yy108;
+ if (yych <= '"') goto yy43;
+ } else {
+ if (yych == ':') goto yy65;
+ goto yy43;
+ }
+ }
+yy108:
+ YYDEBUG(108, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 120 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, ADDR_PARAM);
+ yylval->addr = strtoul(yytext, 0, 16);
+ return T_ADDR;
+}
+#line 1050 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy109:
+ YYDEBUG(109, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy44;
+ if (yych >= '\v') goto yy44;
+ } else {
+ if (yych <= '\r') goto yy110;
+ if (yych != ' ') goto yy44;
+ }
+yy110:
+ YYDEBUG(110, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(111, *YYCURSOR);
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy112;
+ if (yych <= '\n') goto yy110;
+ } else {
+ if (yych <= '\r') goto yy110;
+ if (yych == ' ') goto yy110;
+ }
+yy112:
+ YYDEBUG(112, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 80 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ YYSETCONDITION(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_IF;
+}
+#line 1084 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy113:
+ YYDEBUG(113, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= ' ') {
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy44;
+ if (yych >= '\v') goto yy44;
+ } else {
+ if (yych <= '\r') goto yy114;
+ if (yych <= 0x1F) goto yy44;
+ }
+ } else {
+ if (yych <= '.') {
+ if (yych <= ',') goto yy44;
+ if (yych <= '-') goto yy116;
+ goto yy117;
+ } else {
+ if (yych <= '/') goto yy44;
+ if (yych <= '9') goto yy117;
+ goto yy44;
+ }
+ }
+yy114:
+ YYDEBUG(114, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(2);
+ yych = *YYCURSOR;
+ YYDEBUG(115, *YYCURSOR);
+ if (yych <= ' ') {
+ if (yych <= '\f') {
+ if (yych <= 0x08) goto yy66;
+ if (yych <= '\n') goto yy114;
+ goto yy66;
+ } else {
+ if (yych <= '\r') goto yy114;
+ if (yych <= 0x1F) goto yy66;
+ goto yy114;
+ }
+ } else {
+ if (yych <= '.') {
+ if (yych <= ',') goto yy66;
+ if (yych <= '-') goto yy120;
+ goto yy121;
+ } else {
+ if (yych <= '/') goto yy66;
+ if (yych <= '9') goto yy121;
+ goto yy66;
+ }
+ }
+yy116:
+ YYDEBUG(116, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == '.') goto yy117;
+ if (yych <= '/') goto yy44;
+ if (yych >= ':') goto yy44;
+yy117:
+ YYDEBUG(117, *YYCURSOR);
+ yyaccept = 4;
+ YYMARKER = ++YYCURSOR;
+ YYFILL(3);
+ yych = *YYCURSOR;
+ YYDEBUG(118, *YYCURSOR);
+ if (yych <= ' ') {
+ if (yych <= '\n') {
+ if (yych <= 0x00) goto yy119;
+ if (yych <= 0x08) goto yy43;
+ } else {
+ if (yych == '\r') goto yy119;
+ if (yych <= 0x1F) goto yy43;
+ }
+ } else {
+ if (yych <= '.') {
+ if (yych == '#') goto yy119;
+ if (yych <= '-') goto yy43;
+ goto yy117;
+ } else {
+ if (yych <= '/') goto yy43;
+ if (yych <= '9') goto yy117;
+ if (yych <= ':') goto yy65;
+ goto yy43;
+ }
+ }
+yy119:
+ YYDEBUG(119, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 73 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ char *text = yytext + 2;
+ while (*++text < '0');
+ yylval->num = atoi(text);
+ return T_REQ_ID;
+}
+#line 1179 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy120:
+ YYDEBUG(120, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yych == '.') goto yy121;
+ if (yych <= '/') goto yy66;
+ if (yych >= ':') goto yy66;
+yy121:
+ YYDEBUG(121, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(122, *YYCURSOR);
+ if (yych == '.') goto yy121;
+ if (yych <= '/') goto yy119;
+ if (yych <= '9') goto yy121;
+ goto yy119;
+ }
+/* *********************************** */
+yyc_PRE_RAW:
+ {
+ static const unsigned char yybm[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 160, 48, 0, 0, 160, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 160, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 64, 0,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ YYDEBUG(123, *YYCURSOR);
+ YYFILL(2);
+ yych = *YYCURSOR;
+ if (yybm[0+yych] & 16) {
+ goto yy127;
+ }
+ if (yych <= '\r') {
+ if (yych <= 0x08) {
+ if (yych <= 0x00) goto yy130;
+ goto yy132;
+ } else {
+ if (yych <= '\t') goto yy125;
+ if (yych <= '\f') goto yy132;
+ }
+ } else {
+ if (yych <= ' ') {
+ if (yych <= 0x1F) goto yy132;
+ } else {
+ if (yych == '-') goto yy131;
+ goto yy132;
+ }
+ }
+yy125:
+ YYDEBUG(125, *YYCURSOR);
+ ++YYCURSOR;
+ if ((yych = *YYCURSOR) <= '\f') {
+ if (yych <= 0x00) goto yy142;
+ if (yych <= 0x08) goto yy126;
+ if (yych <= '\n') goto yy142;
+ } else {
+ if (yych <= '\r') goto yy142;
+ if (yych == ' ') goto yy142;
+ }
+yy126:
+ YYDEBUG(126, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 169 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ YYSETCONDITION(RAW);
+
+ YYCURSOR = LEX(text);
+ goto restart;
+}
+#line 1277 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy127:
+ YYDEBUG(127, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(128, *YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy141;
+ }
+ if (yych <= 0x00) goto yy140;
+ if (yych == '\n') goto yy127;
+yy129:
+ YYDEBUG(129, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 69 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return 0;
+}
+#line 1296 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy130:
+ YYDEBUG(130, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy126;
+yy131:
+ YYDEBUG(131, *YYCURSOR);
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'r') goto yy133;
+ goto yy126;
+yy132:
+ YYDEBUG(132, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy126;
+yy133:
+ YYDEBUG(133, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(2);
+ yych = *YYCURSOR;
+ YYDEBUG(134, *YYCURSOR);
+ if (yybm[0+yych] & 32) {
+ goto yy133;
+ }
+ if (yych <= '.') {
+ if (yych <= ',') goto yy135;
+ if (yych <= '-') goto yy136;
+ goto yy137;
+ } else {
+ if (yych <= '/') goto yy135;
+ if (yych <= '9') goto yy137;
+ }
+yy135:
+ YYDEBUG(135, *YYCURSOR);
+ YYCURSOR = YYMARKER;
+ goto yy126;
+yy136:
+ YYDEBUG(136, *YYCURSOR);
+ yych = *++YYCURSOR;
+ if (yybm[0+yych] & 64) {
+ goto yy137;
+ }
+ goto yy135;
+yy137:
+ YYDEBUG(137, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(138, *YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy137;
+ }
+ YYDEBUG(139, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 73 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ char *text = yytext + 2;
+ while (*++text < '0');
+ yylval->num = atoi(text);
+ return T_REQ_ID;
+}
+#line 1357 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy140:
+ YYDEBUG(140, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy129;
+yy141:
+ YYDEBUG(141, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+yy142:
+ YYDEBUG(142, *YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy141;
+ }
+ if (yych <= 0x00) goto yy140;
+ if (yych == '\n') goto yy127;
+ YYDEBUG(143, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 147 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ /* ignore whitespace */
+
+ goto restart;
+}
+#line 1382 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+ }
+/* *********************************** */
+yyc_RAW:
+ {
+ static const unsigned char yybm[] = {
+ 0, 128, 128, 128, 128, 128, 128, 128,
+ 128, 160, 64, 128, 128, 160, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 160, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ };
+ YYDEBUG(144, *YYCURSOR);
+ YYFILL(1);
+ yych = *YYCURSOR;
+ if (yybm[0+yych] & 32) {
+ goto yy146;
+ }
+ if (yych <= 0x00) goto yy152;
+ if (yych == '\n') goto yy149;
+ goto yy153;
+yy146:
+ YYDEBUG(146, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(147, *YYCURSOR);
+ if (yybm[0+yych] & 32) {
+ goto yy146;
+ }
+ if (yych <= 0x00) goto yy152;
+ if (yych == '\n') goto yy149;
+ goto yy153;
+yy148:
+ YYDEBUG(148, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 140 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_INPUT;
+}
+#line 1452 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy149:
+ YYDEBUG(149, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(150, *YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy149;
+ }
+ if (yych <= '\f') {
+ if (yych <= 0x00) goto yy152;
+ if (yych == '\t') goto yy155;
+ } else {
+ if (yych <= '\r') goto yy155;
+ if (yych == ' ') goto yy155;
+ }
+yy151:
+ YYDEBUG(151, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 69 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ return 0;
+}
+#line 1476 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+yy152:
+ YYDEBUG(152, *YYCURSOR);
+ yych = *++YYCURSOR;
+ goto yy151;
+yy153:
+ YYDEBUG(153, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(154, *YYCURSOR);
+ if (yybm[0+yych] & 128) {
+ goto yy153;
+ }
+ goto yy148;
+yy155:
+ YYDEBUG(155, *YYCURSOR);
+ ++YYCURSOR;
+ YYFILL(1);
+ yych = *YYCURSOR;
+ YYDEBUG(156, *YYCURSOR);
+ if (yybm[0+yych] & 64) {
+ goto yy149;
+ }
+ if (yych <= '\f') {
+ if (yych <= 0x00) goto yy152;
+ if (yych == '\t') goto yy155;
+ } else {
+ if (yych <= '\r') goto yy155;
+ if (yych == ' ') goto yy155;
+ }
+ YYDEBUG(157, *YYCURSOR);
+ yyleng = (size_t) YYCURSOR - (size_t) yytext;
+#line 147 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+ {
+ /* ignore whitespace */
+
+ goto restart;
+}
+#line 1515 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.c"
+ }
+}
+#line 183 "/Users/Bob/php-src-5.6/sapi/phpdbg/phpdbg_lexer.l"
+
+}
diff --git a/sapi/phpdbg/phpdbg_lexer.h b/sapi/phpdbg/phpdbg_lexer.h
new file mode 100644
index 0000000000..ab51e7daa8
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_LEXER_H
+#define PHPDBG_LEXER_H
+
+#include "phpdbg_cmd.h"
+
+typedef struct {
+ unsigned int len;
+ unsigned char *text;
+ unsigned char *cursor;
+ unsigned char *marker;
+ int state;
+} phpdbg_lexer_data;
+
+#define yyparse phpdbg_parse
+#define yylex phpdbg_lex
+
+void phpdbg_init_lexer (phpdbg_param_t *stack, char *input TSRMLS_DC);
+
+int phpdbg_lex (phpdbg_param_t* yylval);
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_lexer.l b/sapi/phpdbg/phpdbg_lexer.l
new file mode 100644
index 0000000000..0c27fc22ac
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.l
@@ -0,0 +1,184 @@
+/*
+ * phpdbg_lexer.l
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+
+#include "phpdbg_parser.h"
+
+#define LEX(v) (PHPDBG_G(lexer).v)
+
+#define YYCTYPE unsigned char
+#define YYSETCONDITION(x) LEX(state) = x;
+#define YYGETCONDITION() LEX(state)
+#define YYCURSOR LEX(cursor)
+#define YYMARKER LEX(marker)
+#define yyleng LEX(len)
+#define yytext ((char*) LEX(text))
+#undef YYDEBUG
+#define YYDEBUG(a, b)
+#define YYFILL(n)
+
+#define NORMAL 0
+#define PRE_RAW 1
+#define RAW 2
+#define INITIAL 3
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+void phpdbg_init_lexer (phpdbg_param_t *stack, char *input TSRMLS_DC) {
+ PHPDBG_G(parser_stack) = stack;
+
+ YYSETCONDITION(INITIAL);
+
+ LEX(text) = YYCURSOR = (unsigned char *) input;
+ LEX(len) = strlen(input);
+}
+
+int phpdbg_lex (phpdbg_param_t* yylval) {
+ TSRMLS_FETCH(); /* Slow, but this is not a major problem here. TODO: Use TSRMLS_DC */
+
+restart:
+ LEX(text) = YYCURSOR;
+
+/*!re2c
+re2c:yyfill:check = 0;
+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:#\000]+
+ADDR [0][x][a-fA-F0-9]+
+OPCODE (ZEND_|zend_)([A-Za-z])+
+INPUT [^\n\000]+
+
+<!*> := yyleng = (size_t) YYCURSOR - (size_t) yytext;
+
+<*>{WS}?[\n\000] {
+ return 0;
+}
+
+<PRE_RAW, NORMAL>[-][r]{WS}?{DIGITS} {
+ char *text = yytext + 2;
+ while (*++text < '0');
+ yylval->num = atoi(text);
+ return T_REQ_ID;
+}
+
+<NORMAL>{T_IF}{WS} {
+ YYSETCONDITION(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_IF;
+}
+
+<NORMAL>{ID}[:]{1}[//]{2} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_PROTO;
+}
+<NORMAL>[#]{1} {
+ return T_POUND;
+}
+<NORMAL>[:]{2} {
+ return T_DCOLON;
+}
+<NORMAL>[:]{1} {
+ return T_COLON;
+}
+
+<NORMAL>({T_YES}|{T_ON}|{T_ENABLED}|{T_TRUE}){WS} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 1;
+ return T_TRUTHY;
+}
+
+<NORMAL>({T_NO}|{T_OFF}|{T_DISABLED}|{T_FALSE}){WS} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 0;
+ return T_FALSY;
+}
+
+<NORMAL>{DIGITS} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = atoi(yytext);
+ return T_DIGITS;
+}
+
+<NORMAL>{ADDR} {
+ phpdbg_init_param(yylval, ADDR_PARAM);
+ yylval->addr = strtoul(yytext, 0, 16);
+ return T_ADDR;
+}
+
+<NORMAL>{OPCODE} {
+ phpdbg_init_param(yylval, OP_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_OPCODE;
+}
+
+<NORMAL>{ID} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_ID;
+}
+
+<RAW>{INPUT} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_INPUT;
+}
+
+<*>{WS} {
+ /* ignore whitespace */
+
+ goto restart;
+}
+
+<INITIAL>{T_EVAL}{WS} {
+ YYSETCONDITION(PRE_RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_EVAL;
+}
+<INITIAL>{T_SHELL}{WS} {
+ YYSETCONDITION(PRE_RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_SHELL;
+}
+<INITIAL>({T_RUN}|{T_RUN_SHORT}){WS} {
+ YYSETCONDITION(PRE_RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_RUN;
+}
+
+<PRE_RAW>. {
+ YYSETCONDITION(RAW);
+
+ YYCURSOR = LEX(text);
+ goto restart;
+}
+
+<INITIAL>. {
+ YYSETCONDITION(NORMAL);
+
+ YYCURSOR = LEX(text);
+ goto restart;
+}
+
+*/
+}
diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c
new file mode 100644
index 0000000000..7aa8c4f9e0
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_list.c
@@ -0,0 +1,306 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+# include <sys/mman.h>
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#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, flags) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13], flags)
+
+const phpdbg_command_t phpdbg_list_commands[] = {
+ PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l", PHPDBG_ASYNC_SAFE),
+ PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m", PHPDBG_ASYNC_SAFE),
+ PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_END_COMMAND
+};
+
+PHPDBG_LIST(lines) /* {{{ */
+{
+ if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) {
+ phpdbg_error("inactive", "type=\"execution\"", "Not executing, and execution context not set");
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case NUMERIC_PARAM:
+ phpdbg_list_file(phpdbg_current_file(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;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_LIST(func) /* {{{ */
+{
+ phpdbg_list_function_byname(param->str, param->len TSRMLS_CC);
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_LIST(method) /* {{{ */
+{
+ zend_class_entry **ce;
+
+ if (phpdbg_safe_class_lookup(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));
+
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
+ phpdbg_list_function(function TSRMLS_CC);
+ } else {
+ phpdbg_error("list", "type=\"notfound\" method=\"%s::%s\"", "Could not find %s::%s", param->method.class, param->method.name);
+ }
+
+ efree(lcname);
+ } else {
+ phpdbg_error("list", "type=\"notfound\" class=\"%s\"", "Could not find the class %s", param->method.class);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_LIST(class) /* {{{ */
+{
+ zend_class_entry **ce;
+
+ if (phpdbg_safe_class_lookup(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("list", "type=\"nosource\" class=\"%s\"", "The source of the requested class (%s) cannot be found", (*ce)->name);
+ }
+ } else {
+ phpdbg_error("list", "type=\"internalclass\" class=\"%s\"", "The class requested (%s) is not user defined", (*ce)->name);
+ }
+ } else {
+ phpdbg_error("list", "type=\"notfound\" class=\"%s\"", "The requested class (%s) could not be found", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+void phpdbg_list_file(const char *filename, uint count, int offset, uint highlight TSRMLS_DC) /* {{{ */
+{
+ uint line, lastline;
+ phpdbg_file_source **data;
+ char resolved_path_buf[MAXPATHLEN];
+
+ if (VCWD_REALPATH(filename, resolved_path_buf)) {
+ filename = resolved_path_buf;
+ }
+
+ if (zend_hash_find(&PHPDBG_G(file_sources), filename, strlen(filename), (void **) &data) == FAILURE) {
+ phpdbg_error("list", "type=\"unknownfile\"", "Could not find information about included file...");
+ return;
+ }
+
+ if (offset < 0) {
+ count += offset;
+ offset = 0;
+ }
+
+ lastline = offset + count;
+
+ if (lastline > (*data)->lines) {
+ lastline = (*data)->lines;
+ }
+
+ phpdbg_xml("<list %r file=\"%s\">", filename);
+
+ for (line = offset; line < lastline;) {
+ uint linestart = (*data)->line[line++];
+ uint linelen = (*data)->line[line] - linestart;
+ char *buffer = (*data)->buf + linestart;
+
+ if (!highlight) {
+ phpdbg_write("line", "line=\"%u\" code=\"%.*s\"", " %05u: %.*s", line, linelen, buffer);
+ } else {
+ if (highlight != line) {
+ phpdbg_write("line", "line=\"%u\" code=\"%.*s\"", " %05u: %.*s", line, linelen, buffer);
+ } else {
+ phpdbg_write("line", "line=\"%u\" code=\"%.*s\" current=\"current\"", ">%05u: %.*s", line, linelen, buffer);
+ }
+ }
+
+ if (*(buffer + linelen - 1) != '\n' || !linelen) {
+ phpdbg_out("\n");
+ }
+ }
+
+ phpdbg_xml("</list>");
+} /* }}} */
+
+void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
+{
+ const zend_op_array *ops;
+
+ if (fbc->type != ZEND_USER_FUNCTION) {
+ phpdbg_error("list", "type=\"internalfunction\" function=\"%s\"", "The function requested (%s) is not user defined", fbc->common.function_name);
+ return;
+ }
+
+ ops = (zend_op_array *)fbc;
+
+ phpdbg_list_file(ops->filename, ops->line_end - ops->line_start + 1, ops->line_start, 0 TSRMLS_CC);
+} /* }}} */
+
+void phpdbg_list_function_byname(const char *str, size_t len TSRMLS_DC) /* {{{ */
+{
+ HashTable *func_table = EG(function_table);
+ zend_function* fbc;
+ char *func_name = (char*) str;
+ size_t func_name_len = len;
+
+ /* 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("inactive", "type=\"noclasses\"", "No active class");
+ return;
+ }
+ } else if (!EG(function_table)) {
+ phpdbg_error("inactive", "type=\"function_table\"", "No function table loaded");
+ return;
+ } else {
+ func_table = EG(function_table);
+ }
+
+ /* use lowercase names, case insensitive */
+ func_name = zend_str_tolower_dup(func_name, func_name_len);
+
+ phpdbg_try_access {
+ if (zend_hash_find(func_table, func_name, func_name_len+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_list_function(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("list", "type=\"nofunction\" function=\"%s\"", "Function %s not found", func_name);
+ }
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "function=\"%s\"", "Could not list function %s, invalid data source", func_name);
+ } phpdbg_end_try_access();
+
+ efree(func_name);
+} /* }}} */
+
+zend_op_array *phpdbg_compile_file(zend_file_handle *file, int type TSRMLS_DC) {
+ phpdbg_file_source data, *dataptr;
+ zend_file_handle fake = {0};
+ zend_op_array *ret;
+ char *filename = (char *)(file->opened_path ? file->opened_path : file->filename);
+ uint line;
+ char *bufptr, *endptr;
+ char resolved_path_buf[MAXPATHLEN];
+
+ zend_stream_fixup(file, &data.buf, &data.len TSRMLS_CC);
+
+ data.filename = filename;
+ data.line[0] = 0;
+
+ if (file->handle.stream.mmap.old_closer) {
+ /* do not unmap */
+ file->handle.stream.closer = file->handle.stream.mmap.old_closer;
+ }
+
+#if HAVE_MMAP
+ if (file->handle.stream.mmap.map) {
+ data.map = file->handle.stream.mmap.map;
+ }
+#endif
+
+ fake.type = ZEND_HANDLE_MAPPED;
+ fake.handle.stream.mmap.buf = data.buf;
+ fake.handle.stream.mmap.len = data.len;
+ fake.free_filename = 0;
+ fake.opened_path = file->opened_path;
+ fake.filename = filename;
+ fake.opened_path = file->opened_path;
+
+ *(dataptr = emalloc(sizeof(phpdbg_file_source) + sizeof(uint) * data.len)) = data;
+ if (VCWD_REALPATH(filename, resolved_path_buf)) {
+ filename = resolved_path_buf;
+ }
+ zend_hash_add(&PHPDBG_G(file_sources), filename, strlen(filename), &dataptr, sizeof(phpdbg_file_source *), NULL);
+
+ for (line = 0, bufptr = data.buf - 1, endptr = data.buf + data.len; ++bufptr < endptr;) {
+ if (*bufptr == '\n') {
+ dataptr->line[++line] = (uint)(bufptr - data.buf) + 1;
+ }
+ }
+ dataptr->lines = ++line;
+ dataptr->line[line] = endptr - data.buf;
+ dataptr = erealloc(dataptr, sizeof(phpdbg_file_source) + sizeof(uint) * line);
+
+ ret = PHPDBG_G(compile_file)(&fake, type TSRMLS_CC);
+
+ fake.opened_path = NULL;
+ zend_file_handle_dtor(&fake TSRMLS_CC);
+
+ return ret;
+}
+
+void phpdbg_free_file_source(phpdbg_file_source *data) {
+#if HAVE_MMAP
+ if (data->map) {
+ munmap(data->map, data->len + ZEND_MMAP_AHEAD);
+ } else
+#endif
+ if (data->buf) {
+ efree(data->buf);
+ }
+
+ efree(data);
+}
+
+void phpdbg_init_list(TSRMLS_D) {
+ PHPDBG_G(compile_file) = zend_compile_file;
+ zend_hash_init(&PHPDBG_G(file_sources), 1, NULL, (dtor_func_t) phpdbg_free_file_source, 0);
+ zend_compile_file = phpdbg_compile_file;
+}
diff --git a/sapi/phpdbg/phpdbg_list.h b/sapi/phpdbg/phpdbg_list.h
new file mode 100644
index 0000000000..c88328bfea
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_list.h
@@ -0,0 +1,54 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_LIST_H
+#define PHPDBG_LIST_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_LIST(name) PHPDBG_COMMAND(list_##name)
+#define PHPDBG_LIST_HANDLER(name) PHPDBG_COMMAND_HANDLER(list_##name)
+
+PHPDBG_LIST(lines);
+PHPDBG_LIST(class);
+PHPDBG_LIST(method);
+PHPDBG_LIST(func);
+
+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*, uint, int, uint TSRMLS_DC);
+
+extern const phpdbg_command_t phpdbg_list_commands[];
+
+void phpdbg_init_list(TSRMLS_D);
+
+typedef struct {
+ char *filename;
+ char *buf;
+ size_t len;
+#if HAVE_MMAP
+ void *map;
+#endif
+ uint lines;
+ uint line[1];
+} phpdbg_file_source;
+
+#endif /* PHPDBG_LIST_H */
diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c
new file mode 100644
index 0000000000..331718accc
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_opcode.c
@@ -0,0 +1,356 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "zend_vm_opcodes.h"
+#include "zend_compile.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_utils.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+static inline zend_uint phpdbg_decode_literal(zend_op_array *ops, zend_literal *literal TSRMLS_DC) /* {{{ */
+{
+ int iter = 0;
+
+ while (iter < ops->last_literal) {
+ if (literal == &ops->literals[iter]) {
+ return iter;
+ }
+ iter++;
+ }
+
+ return 0;
+} /* }}} */
+
+static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */
+{
+ char *decode = NULL;
+
+ switch (type &~ EXT_TYPE_UNUSED) {
+ case IS_CV:
+ asprintf(&decode, "$%s", ops->vars[op->var].name);
+ break;
+
+ case IS_VAR:
+ case IS_TMP_VAR: {
+ zend_ulong id = 0, *pid = NULL;
+ if (vars != NULL) {
+ if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) {
+ id = zend_hash_num_elements(vars);
+ zend_hash_index_update(
+ vars, (zend_ulong) ops->vars - op->var,
+ (void**) &id,
+ sizeof(zend_ulong), NULL);
+ } else id = *pid;
+ }
+ asprintf(&decode, "@%lu", id);
+ } break;
+
+ case IS_CONST:
+ asprintf(&decode, "C%u", phpdbg_decode_literal(ops, op->literal TSRMLS_CC));
+ break;
+
+ case IS_UNUSED:
+ asprintf(&decode, "<unused>");
+ break;
+ }
+ return decode;
+} /* }}} */
+
+char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */
+{
+ char *decode[4] = {NULL, NULL, NULL, NULL};
+
+ switch (op->opcode) {
+ case ZEND_JMP:
+#ifdef ZEND_GOTO
+ case ZEND_GOTO:
+#endif
+#ifdef ZEND_FAST_CALL
+ case ZEND_FAST_CALL:
+#endif
+ asprintf(&decode[1], "J%ld", op->op1.jmp_addr - ops->opcodes);
+ goto format;
+
+ case ZEND_JMPZNZ:
+ decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
+ asprintf(&decode[2], "J%u or J%lu", op->op2.opline_num, op->extended_value);
+ goto result;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+
+#ifdef ZEND_JMP_SET
+ case ZEND_JMP_SET:
+#endif
+ decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
+ asprintf(&decode[2], "J%ld", op->op2.jmp_addr - ops->opcodes);
+ goto result;
+
+ case ZEND_RECV_INIT:
+ goto result;
+
+ default: {
+ decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
+ decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC);
+result:
+ decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars TSRMLS_CC);
+format:
+ asprintf(&decode[0],
+ "%-20s %-20s %-20s",
+ decode[1] ? decode[1] : "",
+ decode[2] ? decode[2] : "",
+ decode[3] ? decode[3] : "");
+ }
+ }
+
+ if (decode[1])
+ free(decode[1]);
+ if (decode[2])
+ free(decode[2]);
+ if (decode[3])
+ free(decode[3]);
+
+ return decode[0];
+} /* }}} */
+
+void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
+{
+ /* force out a line while stepping so the user knows what is happening */
+ if (ignore_flags ||
+ (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
+ (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ||
+ (PHPDBG_G(oplog)))) {
+
+ zend_op *opline = execute_data->opline;
+ char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC);
+
+ if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
+ /* output line info */
+ phpdbg_notice("opline", "line=\"%u\" opline=\"%p\" opcode=\"%s\" op=\"%s\" file=\"%s\"", "L%-5u %16p %-30s %s %s",
+ opline->lineno,
+ opline,
+ phpdbg_decode_opcode(opline->opcode),
+ decode,
+ execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
+ }
+
+ if (!ignore_flags && PHPDBG_G(oplog)) {
+ phpdbg_log_ex(fileno(PHPDBG_G(oplog)), "L%-5u %16p %-30s %s %s",
+ opline->lineno,
+ opline,
+ phpdbg_decode_opcode(opline->opcode),
+ decode,
+ execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
+ }
+
+ if (decode) {
+ free(decode);
+ }
+ }
+} /* }}} */
+
+void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
+{
+ phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC);
+} /* }}} */
+
+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);
+ CASE(ZEND_ADD);
+ CASE(ZEND_SUB);
+ CASE(ZEND_MUL);
+ CASE(ZEND_DIV);
+ CASE(ZEND_MOD);
+ CASE(ZEND_SL);
+ CASE(ZEND_SR);
+ CASE(ZEND_CONCAT);
+ CASE(ZEND_BW_OR);
+ CASE(ZEND_BW_AND);
+ CASE(ZEND_BW_XOR);
+ CASE(ZEND_BW_NOT);
+ CASE(ZEND_BOOL_NOT);
+ CASE(ZEND_BOOL_XOR);
+ CASE(ZEND_IS_IDENTICAL);
+ CASE(ZEND_IS_NOT_IDENTICAL);
+ CASE(ZEND_IS_EQUAL);
+ CASE(ZEND_IS_NOT_EQUAL);
+ CASE(ZEND_IS_SMALLER);
+ CASE(ZEND_IS_SMALLER_OR_EQUAL);
+ CASE(ZEND_CAST);
+ CASE(ZEND_QM_ASSIGN);
+ CASE(ZEND_ASSIGN_ADD);
+ CASE(ZEND_ASSIGN_SUB);
+ CASE(ZEND_ASSIGN_MUL);
+ CASE(ZEND_ASSIGN_DIV);
+ CASE(ZEND_ASSIGN_MOD);
+ CASE(ZEND_ASSIGN_SL);
+ CASE(ZEND_ASSIGN_SR);
+ CASE(ZEND_ASSIGN_CONCAT);
+ CASE(ZEND_ASSIGN_BW_OR);
+ CASE(ZEND_ASSIGN_BW_AND);
+ CASE(ZEND_ASSIGN_BW_XOR);
+ CASE(ZEND_PRE_INC);
+ CASE(ZEND_PRE_DEC);
+ CASE(ZEND_POST_INC);
+ CASE(ZEND_POST_DEC);
+ CASE(ZEND_ASSIGN);
+ CASE(ZEND_ASSIGN_REF);
+ CASE(ZEND_ECHO);
+ CASE(ZEND_PRINT);
+ CASE(ZEND_JMP);
+ CASE(ZEND_JMPZ);
+ CASE(ZEND_JMPNZ);
+ CASE(ZEND_JMPZNZ);
+ CASE(ZEND_JMPZ_EX);
+ CASE(ZEND_JMPNZ_EX);
+ CASE(ZEND_CASE);
+ CASE(ZEND_SWITCH_FREE);
+ CASE(ZEND_BRK);
+ CASE(ZEND_CONT);
+ CASE(ZEND_BOOL);
+ CASE(ZEND_INIT_STRING);
+ CASE(ZEND_ADD_CHAR);
+ CASE(ZEND_ADD_STRING);
+ CASE(ZEND_ADD_VAR);
+ CASE(ZEND_BEGIN_SILENCE);
+ CASE(ZEND_END_SILENCE);
+ CASE(ZEND_INIT_FCALL_BY_NAME);
+ CASE(ZEND_DO_FCALL);
+ CASE(ZEND_DO_FCALL_BY_NAME);
+ CASE(ZEND_RETURN);
+ CASE(ZEND_RECV);
+ CASE(ZEND_RECV_INIT);
+ CASE(ZEND_SEND_VAL);
+ CASE(ZEND_SEND_VAR);
+ CASE(ZEND_SEND_REF);
+ CASE(ZEND_NEW);
+ CASE(ZEND_INIT_NS_FCALL_BY_NAME);
+ CASE(ZEND_FREE);
+ CASE(ZEND_INIT_ARRAY);
+ CASE(ZEND_ADD_ARRAY_ELEMENT);
+ CASE(ZEND_INCLUDE_OR_EVAL);
+ CASE(ZEND_UNSET_VAR);
+ CASE(ZEND_UNSET_DIM);
+ CASE(ZEND_UNSET_OBJ);
+ CASE(ZEND_FE_RESET);
+ CASE(ZEND_FE_FETCH);
+ CASE(ZEND_EXIT);
+ CASE(ZEND_FETCH_R);
+ CASE(ZEND_FETCH_DIM_R);
+ CASE(ZEND_FETCH_OBJ_R);
+ CASE(ZEND_FETCH_W);
+ CASE(ZEND_FETCH_DIM_W);
+ CASE(ZEND_FETCH_OBJ_W);
+ CASE(ZEND_FETCH_RW);
+ CASE(ZEND_FETCH_DIM_RW);
+ CASE(ZEND_FETCH_OBJ_RW);
+ CASE(ZEND_FETCH_IS);
+ CASE(ZEND_FETCH_DIM_IS);
+ CASE(ZEND_FETCH_OBJ_IS);
+ CASE(ZEND_FETCH_FUNC_ARG);
+ CASE(ZEND_FETCH_DIM_FUNC_ARG);
+ CASE(ZEND_FETCH_OBJ_FUNC_ARG);
+ CASE(ZEND_FETCH_UNSET);
+ CASE(ZEND_FETCH_DIM_UNSET);
+ CASE(ZEND_FETCH_OBJ_UNSET);
+ CASE(ZEND_FETCH_DIM_TMP_VAR);
+ CASE(ZEND_FETCH_CONSTANT);
+ CASE(ZEND_GOTO);
+ CASE(ZEND_EXT_STMT);
+ CASE(ZEND_EXT_FCALL_BEGIN);
+ CASE(ZEND_EXT_FCALL_END);
+ CASE(ZEND_EXT_NOP);
+ CASE(ZEND_TICKS);
+ CASE(ZEND_SEND_VAR_NO_REF);
+ CASE(ZEND_CATCH);
+ CASE(ZEND_THROW);
+ CASE(ZEND_FETCH_CLASS);
+ CASE(ZEND_CLONE);
+ CASE(ZEND_RETURN_BY_REF);
+ CASE(ZEND_INIT_METHOD_CALL);
+ CASE(ZEND_INIT_STATIC_METHOD_CALL);
+ CASE(ZEND_ISSET_ISEMPTY_VAR);
+ CASE(ZEND_ISSET_ISEMPTY_DIM_OBJ);
+ CASE(ZEND_PRE_INC_OBJ);
+ CASE(ZEND_PRE_DEC_OBJ);
+ CASE(ZEND_POST_INC_OBJ);
+ CASE(ZEND_POST_DEC_OBJ);
+ CASE(ZEND_ASSIGN_OBJ);
+ CASE(ZEND_INSTANCEOF);
+ CASE(ZEND_DECLARE_CLASS);
+ CASE(ZEND_DECLARE_INHERITED_CLASS);
+ CASE(ZEND_DECLARE_FUNCTION);
+ CASE(ZEND_RAISE_ABSTRACT_ERROR);
+ CASE(ZEND_DECLARE_CONST);
+ CASE(ZEND_ADD_INTERFACE);
+ CASE(ZEND_DECLARE_INHERITED_CLASS_DELAYED);
+ CASE(ZEND_VERIFY_ABSTRACT_CLASS);
+ CASE(ZEND_ASSIGN_DIM);
+ CASE(ZEND_ISSET_ISEMPTY_PROP_OBJ);
+ CASE(ZEND_HANDLE_EXCEPTION);
+ CASE(ZEND_USER_OPCODE);
+#ifdef ZEND_JMP_SET
+ CASE(ZEND_JMP_SET);
+#endif
+ CASE(ZEND_DECLARE_LAMBDA_FUNCTION);
+#ifdef ZEND_ADD_TRAIT
+ CASE(ZEND_ADD_TRAIT);
+#endif
+#ifdef ZEND_BIND_TRAITS
+ CASE(ZEND_BIND_TRAITS);
+#endif
+#ifdef ZEND_SEPARATE
+ CASE(ZEND_SEPARATE);
+#endif
+#ifdef ZEND_DISCARD_EXCEPTION
+ CASE(ZEND_DISCARD_EXCEPTION);
+#endif
+#ifdef ZEND_YIELD
+ CASE(ZEND_YIELD);
+#endif
+#ifdef ZEND_GENERATOR_RETURN
+ CASE(ZEND_GENERATOR_RETURN);
+#endif
+#ifdef ZEND_FAST_CALL
+ CASE(ZEND_FAST_CALL);
+#endif
+#ifdef ZEND_FAST_RET
+ CASE(ZEND_FAST_RET);
+#endif
+#ifdef ZEND_RECV_VARIADIC
+ CASE(ZEND_RECV_VARIADIC);
+#endif
+ CASE(ZEND_OP_DATA);
+ default:
+ return "UNKNOWN";
+ }
+#else
+ const char *ret = zend_get_opcode_name(opcode);
+ return ret?ret:"UNKNOWN";
+#endif
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_opcode.h b/sapi/phpdbg/phpdbg_opcode.h
new file mode 100644
index 0000000000..144442981d
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_opcode.h
@@ -0,0 +1,31 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_OPCODE_H
+#define PHPDBG_OPCODE_H
+
+#include "zend_types.h"
+
+const char *phpdbg_decode_opcode(zend_uchar);
+char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC);
+void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC);
+void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC);
+
+#endif /* PHPDBG_OPCODE_H */
diff --git a/sapi/phpdbg/phpdbg_out.c b/sapi/phpdbg/phpdbg_out.c
new file mode 100644
index 0000000000..a4793f144f
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_out.c
@@ -0,0 +1,1305 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "php.h"
+#include "spprintf.h"
+#include "phpdbg.h"
+#include "phpdbg_io.h"
+#include "phpdbg_eol.h"
+#include "ext/standard/html.h"
+
+#ifdef _WIN32
+# include "win32/time.h"
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+/* copied from php-src/main/snprintf.c and slightly modified */
+/*
+ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
+ *
+ * XXX: this is a magic number; do not decrease it
+ * Emax = 1023
+ * NDIG = 320
+ * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1;
+ */
+#define NUM_BUF_SIZE 2048
+
+/*
+ * Descriptor for buffer area
+ */
+struct buf_area {
+ char *buf_end;
+ char *nextb; /* pointer to next byte to read/write */
+};
+
+typedef struct buf_area buffy;
+
+/*
+ * The INS_CHAR macro inserts a character in the buffer and writes
+ * the buffer back to disk if necessary
+ * It uses the char pointers sp and bep:
+ * sp points to the next available character in the buffer
+ * bep points to the end-of-buffer+1
+ * While using this macro, note that the nextb pointer is NOT updated.
+ *
+ * NOTE: Evaluation of the c argument should not have any side-effects
+ */
+#define INS_CHAR(c, sp, bep, cc) \
+ { \
+ if (sp < bep) \
+ { \
+ *sp++ = c; \
+ } \
+ cc++; \
+ }
+
+#define NUM( c ) ( c - '0' )
+
+#define STR_TO_DEC( str, num ) \
+ num = NUM( *str++ ) ; \
+ while ( isdigit((int)*str ) ) \
+ { \
+ num *= 10 ; \
+ num += NUM( *str++ ) ; \
+ }
+
+/*
+ * This macro does zero padding so that the precision
+ * requirement is satisfied. The padding is done by
+ * adding '0's to the left of the string that is going
+ * to be printed.
+ */
+#define FIX_PRECISION( adjust, precision, s, s_len ) \
+ if ( adjust ) \
+ while ( s_len < precision ) \
+ { \
+ *--s = '0' ; \
+ s_len++ ; \
+ }
+
+/*
+ * Macro that does padding. The padding is done by printing
+ * the character ch.
+ */
+#define PAD( width, len, ch ) do \
+ { \
+ INS_CHAR( ch, sp, bep, cc ) ; \
+ width-- ; \
+ } \
+ while ( width > len )
+
+/*
+ * Prefix the character ch to the string str
+ * Increase length
+ * Set the has_prefix flag
+ */
+#define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES
+
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#define LCONV_DECIMAL_POINT (*lconv->decimal_point)
+#else
+#define LCONV_DECIMAL_POINT '.'
+#endif
+#define NUL '\0'
+#define S_NULL "(null)"
+#define S_NULL_LEN 6
+#define FLOAT_DIGITS 6
+
+/*
+ * Do format conversion placing the output in buffer
+ */
+static int format_converter(register buffy *odp, const char *fmt, zend_bool escape_xml, va_list ap TSRMLS_DC) {
+ char *sp;
+ char *bep;
+ int cc = 0;
+ int i;
+
+ char *s = NULL, *free_s = NULL;
+ int s_len, free_zcopy;
+ zval *zvp, zcopy;
+
+ int min_width = 0;
+ int precision = 0;
+ enum {
+ LEFT, RIGHT
+ } adjust;
+ char pad_char;
+ char prefix_char;
+
+ double fp_num;
+ wide_int i_num = (wide_int) 0;
+ u_wide_int ui_num;
+
+ char num_buf[NUM_BUF_SIZE];
+ char char_buf[2]; /* for printing %% and %<unknown> */
+
+#ifdef HAVE_LOCALE_H
+ struct lconv *lconv = NULL;
+#endif
+
+ /*
+ * Flag variables
+ */
+ length_modifier_e modifier;
+ boolean_e alternate_form;
+ boolean_e print_sign;
+ boolean_e print_blank;
+ boolean_e adjust_precision;
+ boolean_e adjust_width;
+ bool_int is_negative;
+
+ sp = odp->nextb;
+ bep = odp->buf_end;
+
+ while (*fmt) {
+ if (*fmt != '%') {
+ INS_CHAR(*fmt, sp, bep, cc);
+ } else {
+ /*
+ * Default variable settings
+ */
+ adjust = RIGHT;
+ alternate_form = print_sign = print_blank = NO;
+ pad_char = ' ';
+ prefix_char = NUL;
+ free_zcopy = 0;
+
+ fmt++;
+
+ /*
+ * Try to avoid checking for flags, width or precision
+ */
+ if (isascii((int)*fmt) && !islower((int)*fmt)) {
+ /*
+ * Recognize flags: -, #, BLANK, +
+ */
+ for (;; fmt++) {
+ if (*fmt == '-')
+ adjust = LEFT;
+ else if (*fmt == '+')
+ print_sign = YES;
+ else if (*fmt == '#')
+ alternate_form = YES;
+ else if (*fmt == ' ')
+ print_blank = YES;
+ else if (*fmt == '0')
+ pad_char = '0';
+ else
+ break;
+ }
+
+ /*
+ * Check if a width was specified
+ */
+ if (isdigit((int)*fmt)) {
+ STR_TO_DEC(fmt, min_width);
+ adjust_width = YES;
+ } else if (*fmt == '*') {
+ min_width = va_arg(ap, int);
+ fmt++;
+ adjust_width = YES;
+ if (min_width < 0) {
+ adjust = LEFT;
+ min_width = -min_width;
+ }
+ } else
+ adjust_width = NO;
+
+ /*
+ * Check if a precision was specified
+ */
+ if (*fmt == '.') {
+ adjust_precision = YES;
+ fmt++;
+ if (isdigit((int)*fmt)) {
+ STR_TO_DEC(fmt, precision);
+ } else if (*fmt == '*') {
+ precision = va_arg(ap, int);
+ fmt++;
+ if (precision < 0)
+ precision = 0;
+ } else
+ precision = 0;
+
+ if (precision > FORMAT_CONV_MAX_PRECISION && *fmt != 's' && *fmt != 'v' && *fmt != 'b') {
+ precision = FORMAT_CONV_MAX_PRECISION;
+ }
+ } else
+ adjust_precision = NO;
+ } else
+ adjust_precision = adjust_width = NO;
+
+ /*
+ * Modifier check
+ */
+ switch (*fmt) {
+ case 'L':
+ fmt++;
+ modifier = LM_LONG_DOUBLE;
+ break;
+ case 'I':
+ fmt++;
+#if SIZEOF_LONG_LONG
+ if (*fmt == '6' && *(fmt+1) == '4') {
+ fmt += 2;
+ modifier = LM_LONG_LONG;
+ } else
+#endif
+ if (*fmt == '3' && *(fmt+1) == '2') {
+ fmt += 2;
+ modifier = LM_LONG;
+ } else {
+#ifdef _WIN64
+ modifier = LM_LONG_LONG;
+#else
+ modifier = LM_LONG;
+#endif
+ }
+ break;
+ case 'l':
+ fmt++;
+#if SIZEOF_LONG_LONG
+ if (*fmt == 'l') {
+ fmt++;
+ modifier = LM_LONG_LONG;
+ } else
+#endif
+ modifier = LM_LONG;
+ break;
+ case 'z':
+ fmt++;
+ modifier = LM_SIZE_T;
+ break;
+ case 'j':
+ fmt++;
+#if SIZEOF_INTMAX_T
+ modifier = LM_INTMAX_T;
+#else
+ modifier = LM_SIZE_T;
+#endif
+ break;
+ case 't':
+ fmt++;
+#if SIZEOF_PTRDIFF_T
+ modifier = LM_PTRDIFF_T;
+#else
+ modifier = LM_SIZE_T;
+#endif
+ break;
+ case 'h':
+ fmt++;
+ if (*fmt == 'h') {
+ fmt++;
+ }
+ /* these are promoted to int, so no break */
+ default:
+ modifier = LM_STD;
+ break;
+ }
+
+ /*
+ * Argument extraction and printing.
+ * First we determine the argument type.
+ * Then, we convert the argument to a string.
+ * On exit from the switch, s points to the string that
+ * must be printed, s_len has the length of the string
+ * The precision requirements, if any, are reflected in s_len.
+ *
+ * NOTE: pad_char may be set to '0' because of the 0 flag.
+ * It is reset to ' ' by non-numeric formats
+ */
+ switch (*fmt) {
+ case 'Z':
+ zvp = (zval*) va_arg(ap, zval*);
+ zend_make_printable_zval(zvp, &zcopy, &free_zcopy);
+ if (free_zcopy) {
+ zvp = &zcopy;
+ }
+ s_len = Z_STRLEN_P(zvp);
+ s = Z_STRVAL_P(zvp);
+ if (adjust_precision && precision < s_len) {
+ s_len = precision;
+ }
+ break;
+ case 'u':
+ switch(modifier) {
+ default:
+ i_num = (wide_int) va_arg(ap, unsigned int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ i_num = (wide_int) va_arg(ap, unsigned long int);
+ break;
+ case LM_SIZE_T:
+ i_num = (wide_int) va_arg(ap, size_t);
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ i_num = (wide_int) va_arg(ap, u_wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ i_num = (wide_int) va_arg(ap, uintmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ i_num = (wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ /*
+ * The rest also applies to other integer formats, so fall
+ * into that case.
+ */
+ case 'd':
+ case 'i':
+ /*
+ * Get the arg if we haven't already.
+ */
+ if ((*fmt) != 'u') {
+ switch(modifier) {
+ default:
+ i_num = (wide_int) va_arg(ap, int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ i_num = (wide_int) va_arg(ap, long int);
+ break;
+ case LM_SIZE_T:
+#if SIZEOF_SSIZE_T
+ i_num = (wide_int) va_arg(ap, ssize_t);
+#else
+ i_num = (wide_int) va_arg(ap, size_t);
+#endif
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ i_num = (wide_int) va_arg(ap, wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ i_num = (wide_int) va_arg(ap, intmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ i_num = (wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ }
+ s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+ if (*fmt != 'u') {
+ if (is_negative) {
+ prefix_char = '-';
+ } else if (print_sign) {
+ prefix_char = '+';
+ } else if (print_blank) {
+ prefix_char = ' ';
+ }
+ }
+ break;
+
+
+ case 'o':
+ switch(modifier) {
+ default:
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ ui_num = (u_wide_int) va_arg(ap, unsigned long int);
+ break;
+ case LM_SIZE_T:
+ ui_num = (u_wide_int) va_arg(ap, size_t);
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ ui_num = (u_wide_int) va_arg(ap, uintmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && *s != '0') {
+ *--s = '0';
+ s_len++;
+ }
+ break;
+
+
+ case 'x':
+ case 'X':
+ switch(modifier) {
+ default:
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ ui_num = (u_wide_int) va_arg(ap, unsigned long int);
+ break;
+ case LM_SIZE_T:
+ ui_num = (u_wide_int) va_arg(ap, size_t);
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ ui_num = (u_wide_int) va_arg(ap, uintmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && i_num != 0) {
+ *--s = *fmt; /* 'x' or 'X' */
+ *--s = '0';
+ s_len += 2;
+ }
+ break;
+
+
+ case 's':
+ case 'v':
+ s = va_arg(ap, char *);
+ if (s != NULL) {
+ if (adjust_precision) {
+ s_len = precision;
+ } else {
+ s_len = strlen(s);
+ }
+
+ if (escape_xml) {
+ /* added: support for xml escaping */
+
+ int old_slen = s_len, i = 0;
+ char *old_s = s, *s_ptr;
+ free_s = s_ptr = s = emalloc(old_slen * 6 + 1);
+ do {
+ if (old_s[i] == '&' || old_s[i] == '"' || old_s[i] == '<') {
+ *s_ptr++ = '&';
+ switch (old_s[i]) {
+ case '"':
+ s_len += 5;
+ *s_ptr++ = 'q';
+ *s_ptr++ = 'u';
+ *s_ptr++ = 'o';
+ *s_ptr++ = 't';
+ break;
+ case '<':
+ s_len += 3;
+ *s_ptr++ = 'l';
+ *s_ptr++ = 't';
+ break;
+ case '&':
+ s_len += 4;
+ *s_ptr++ = 'a';
+ *s_ptr++ = 'm';
+ *s_ptr++ = 'p';
+ break;
+ }
+ *s_ptr++ = ';';
+ } else {
+ *s_ptr++ = old_s[i];
+ }
+ } while (i++ < old_slen);
+ }
+ } else {
+ s = S_NULL;
+ s_len = S_NULL_LEN;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case 'b':
+ if (escape_xml) {
+ s = PHPDBG_G(err_buf).xml;
+ } else {
+ s = PHPDBG_G(err_buf).msg;
+ }
+
+ if (s != NULL) {
+ if (escape_xml) {
+ s_len = PHPDBG_G(err_buf).xmllen;
+ } else {
+ s_len = PHPDBG_G(err_buf).msglen;
+ }
+
+ if (adjust_precision && precision != s_len) {
+ s_len = precision;
+ }
+ } else {
+ s = "";
+ s_len = 0;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case 'r':
+ if (PHPDBG_G(req_id)) {
+ s_len = spprintf(&s, 0, "req=\"%lu\"", PHPDBG_G(req_id));
+ free_s = s;
+ } else {
+ s = "";
+ s_len = 0;
+ }
+ break;
+
+
+ case 'f':
+ case 'F':
+ case 'e':
+ case 'E':
+
+ switch(modifier) {
+ case LM_LONG_DOUBLE:
+ fp_num = (double) va_arg(ap, long double);
+ break;
+ case LM_STD:
+ fp_num = va_arg(ap, double);
+ break;
+ default:
+ goto fmt_error;
+ }
+
+ if (zend_isnan(fp_num)) {
+ s = "NAN";
+ s_len = 3;
+ } else if (zend_isinf(fp_num)) {
+ s = "INF";
+ s_len = 3;
+ } else {
+#ifdef HAVE_LOCALE_H
+ if (!lconv) {
+ lconv = localeconv();
+ }
+#endif
+ s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
+ (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+ (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
+ &is_negative, &num_buf[1], &s_len);
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ }
+ break;
+
+
+ case 'g':
+ case 'k':
+ case 'G':
+ case 'H':
+ switch(modifier) {
+ case LM_LONG_DOUBLE:
+ fp_num = (double) va_arg(ap, long double);
+ break;
+ case LM_STD:
+ fp_num = va_arg(ap, double);
+ break;
+ default:
+ goto fmt_error;
+ }
+
+ if (zend_isnan(fp_num)) {
+ s = "NAN";
+ s_len = 3;
+ break;
+ } else if (zend_isinf(fp_num)) {
+ if (fp_num > 0) {
+ s = "INF";
+ s_len = 3;
+ } else {
+ s = "-INF";
+ s_len = 4;
+ }
+ break;
+ }
+
+ if (adjust_precision == NO) {
+ precision = FLOAT_DIGITS;
+ } else if (precision == 0) {
+ precision = 1;
+ }
+ /*
+ * * We use &num_buf[ 1 ], so that we have room for the sign
+ */
+#ifdef HAVE_LOCALE_H
+ if (!lconv) {
+ lconv = localeconv();
+ }
+#endif
+ s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
+ if (*s == '-') {
+ prefix_char = *s++;
+ } else if (print_sign) {
+ prefix_char = '+';
+ } else if (print_blank) {
+ prefix_char = ' ';
+ }
+
+ s_len = strlen(s);
+
+ if (alternate_form && (strchr(s, '.')) == NULL) {
+ s[s_len++] = '.';
+ }
+ break;
+
+
+ case 'c':
+ char_buf[0] = (char) (va_arg(ap, int));
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case '%':
+ char_buf[0] = '%';
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case 'n':
+ *(va_arg(ap, int *)) = cc;
+ goto skip_output;
+
+ /*
+ * Always extract the argument as a "char *" pointer. We
+ * should be using "void *" but there are still machines
+ * that don't understand it.
+ * If the pointer size is equal to the size of an unsigned
+ * integer we convert the pointer to a hex number, otherwise
+ * we print "%p" to indicate that we don't handle "%p".
+ */
+ case 'p':
+ if (sizeof(char *) <= sizeof(u_wide_int)) {
+ ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
+ s = ap_php_conv_p2(ui_num, 4, 'x',
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ if (ui_num != 0) {
+ *--s = 'x';
+ *--s = '0';
+ s_len += 2;
+ }
+ } else {
+ s = "%p";
+ s_len = 2;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case NUL:
+ /*
+ * The last character of the format string was %.
+ * We ignore it.
+ */
+ continue;
+
+
+fmt_error:
+ php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
+ /*
+ * The default case is for unrecognized %'s.
+ * We print %<char> to help the user identify what
+ * option is not understood.
+ * This is also useful in case the user wants to pass
+ * the output of format_converter to another function
+ * that understands some other %<char> (like syslog).
+ * Note that we can't point s inside fmt because the
+ * unknown <char> could be preceded by width etc.
+ */
+ default:
+ char_buf[0] = '%';
+ char_buf[1] = *fmt;
+ s = char_buf;
+ s_len = 2;
+ pad_char = ' ';
+ break;
+ }
+
+ if (prefix_char != NUL) {
+ *--s = prefix_char;
+ s_len++;
+ }
+ if (adjust_width && adjust == RIGHT && min_width > s_len) {
+ if (pad_char == '0' && prefix_char != NUL) {
+ INS_CHAR(*s, sp, bep, cc)
+ s++;
+ s_len--;
+ min_width--;
+ }
+ PAD(min_width, s_len, pad_char);
+ }
+ /*
+ * Print the string s.
+ */
+ for (i = s_len; i != 0; i--) {
+ INS_CHAR(*s, sp, bep, cc);
+ s++;
+ }
+
+ if (adjust_width && adjust == LEFT && min_width > s_len)
+ PAD(min_width, s_len, pad_char);
+ if (free_zcopy) {
+ zval_dtor(&zcopy);
+ }
+ }
+skip_output:
+ if (free_s) {
+ efree(free_s);
+ free_s = NULL;
+ }
+
+ fmt++;
+ }
+ odp->nextb = sp;
+ return (cc);
+}
+
+static void strx_printv(int *ccp, char *buf, size_t len, const char *format, zend_bool escape_xml, va_list ap TSRMLS_DC) {
+ buffy od;
+ int cc;
+
+ /*
+ * First initialize the descriptor
+ * Notice that if no length is given, we initialize buf_end to the
+ * highest possible address.
+ */
+ if (len == 0) {
+ od.buf_end = (char *) ~0;
+ od.nextb = (char *) ~0;
+ } else {
+ od.buf_end = &buf[len-1];
+ od.nextb = buf;
+ }
+
+ /*
+ * Do the conversion
+ */
+ cc = format_converter(&od, format, escape_xml, ap TSRMLS_CC);
+ if (len != 0 && od.nextb <= od.buf_end) {
+ *(od.nextb) = '\0';
+ }
+ if (ccp) {
+ *ccp = cc;
+ }
+}
+
+static int phpdbg_xml_vsnprintf(char *buf, size_t len, const char *format, zend_bool escape_xml, va_list ap TSRMLS_DC) {
+ int cc;
+
+ strx_printv(&cc, buf, len, format, escape_xml, ap TSRMLS_CC);
+ return (cc);
+}
+
+PHPDBG_API int phpdbg_xml_vasprintf(char **buf, const char *format, zend_bool escape_xml, va_list ap TSRMLS_DC) {
+ va_list ap2;
+ int cc;
+
+ va_copy(ap2, ap);
+ cc = phpdbg_xml_vsnprintf(NULL, 0, format, escape_xml, ap2 TSRMLS_CC);
+ va_end(ap2);
+
+ *buf = NULL;
+
+ if (cc >= 0) {
+ if ((*buf = emalloc(++cc)) != NULL) {
+ if ((cc = phpdbg_xml_vsnprintf(*buf, cc, format, escape_xml, ap TSRMLS_CC)) < 0) {
+ efree(*buf);
+ *buf = NULL;
+ }
+ }
+ }
+
+ return cc;
+}
+/* copy end */
+
+PHPDBG_API int _phpdbg_xml_asprintf(char **buf TSRMLS_DC, const char *format, zend_bool escape_xml, ...) {
+ int ret;
+ va_list va;
+
+ va_start(va, escape_xml);
+ ret = phpdbg_xml_vasprintf(buf, format, escape_xml, va TSRMLS_CC);
+ va_end(va);
+
+ return ret;
+}
+
+PHPDBG_API int _phpdbg_asprintf(char **buf TSRMLS_DC, const char *format, ...) {
+ int ret;
+ va_list va;
+
+ va_start(va, format);
+ ret = phpdbg_xml_vasprintf(buf, format, 0, va TSRMLS_CC);
+ va_end(va);
+
+ return ret;
+}
+
+static int phpdbg_encode_xml(char **buf, char *msg, int msglen, int from, char *to) {
+ int i;
+ int tolen = to ? strlen(to) : 5;
+ char *tmp = *buf = emalloc(msglen * tolen);
+ for (i = 0; i++ < msglen; msg++) {
+ if (*msg == '&') {
+ memcpy(tmp, ZEND_STRL("&amp;"));
+ tmp += sizeof("&amp;") - 1;
+ } else if (*msg == '<') {
+ memcpy(tmp, ZEND_STRL("&lt;"));
+ tmp += sizeof("&lt;") - 1;
+ } else if (((int) *msg) == from) {
+ memcpy(tmp, to, tolen);
+ tmp += tolen;
+ } else {
+ *tmp++ = *msg;
+ }
+ }
+
+ {
+ int len = tmp - *buf;
+ *buf = erealloc(*buf, len + 1);
+ return len;
+ }
+}
+
+static void phpdbg_encode_ctrl_chars(char **buf, int *buflen) {
+ char *tmp, *tmpptr;
+ int len;
+ int i;
+
+ tmp = tmpptr = emalloc(*buflen * 5);
+
+ for (i = 0; i < *buflen; i++) {
+ if ((*buf)[i] < 0x20) {
+ *tmpptr++ = '&';
+ *tmpptr++ = '#';
+ if ((unsigned int) ((*buf)[i]) > 9) {
+ *tmpptr++ = ((*buf)[i] / 10) + '0';
+ }
+ *tmpptr++ = ((*buf)[i] % 10) + '0';
+ *tmpptr++ = ';';
+ } else {
+ *tmpptr++ = (*buf)[i];
+ }
+ }
+
+ len = tmpptr - tmp;
+
+ efree(*buf);
+ *buf = erealloc(tmp, len + 1);
+ *buflen = len;
+}
+
+static int phpdbg_process_print(int fd, int type, const char *tag, const char *msg, int msglen, const char *xml, int xmllen TSRMLS_DC) {
+ char *msgout = NULL, *buf;
+ int msgoutlen, xmloutlen, buflen;
+ const char *severity;
+
+ if ((PHPDBG_G(flags) & PHPDBG_WRITE_XML) && PHPDBG_G(in_script_xml) && PHPDBG_G(in_script_xml) != type) {
+ phpdbg_mixed_write(fd, ZEND_STRL("</stream>") TSRMLS_CC);
+ PHPDBG_G(in_script_xml) = 0;
+ }
+
+ switch (type) {
+ case P_ERROR:
+ severity = "error";
+ if (!PHPDBG_G(last_was_newline)) {
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ phpdbg_mixed_write(fd, ZEND_STRL("<phpdbg>\n" "</phpdbg>") TSRMLS_CC);
+ } else {
+ phpdbg_mixed_write(fd, ZEND_STRL("\n") TSRMLS_CC);
+ }
+ PHPDBG_G(last_was_newline) = 1;
+ }
+ if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
+ msgoutlen = phpdbg_asprintf(&msgout, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_ERROR]->code, msglen, msg);
+ } else {
+ msgoutlen = phpdbg_asprintf(&msgout, "[%.*s]\n", msglen, msg);
+ }
+ break;
+
+ case P_NOTICE:
+ severity = "notice";
+ if (!PHPDBG_G(last_was_newline)) {
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ phpdbg_mixed_write(fd, ZEND_STRL("<phpdbg>\n" "</phpdbg>") TSRMLS_CC);
+ } else {
+ phpdbg_mixed_write(fd, ZEND_STRL("\n") TSRMLS_CC);
+ }
+ PHPDBG_G(last_was_newline) = 1;
+ }
+ if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
+ msgoutlen = phpdbg_asprintf(&msgout, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_NOTICE]->code, msglen, msg);
+ } else {
+ msgoutlen = phpdbg_asprintf(&msgout, "[%.*s]\n", msglen, msg);
+ }
+ break;
+
+ case P_WRITELN:
+ severity = "normal";
+ if (msg) {
+ msgoutlen = phpdbg_asprintf(&msgout, "%.*s\n", msglen, msg);
+ } else {
+ msgoutlen = 1;
+ msgout = estrdup("\n");
+ }
+ PHPDBG_G(last_was_newline) = 1;
+ break;
+
+ case P_WRITE:
+ severity = "normal";
+ if (msg) {
+ msgout = estrndup(msg, msglen);
+ msgoutlen = msglen;
+ PHPDBG_G(last_was_newline) = msg[msglen - 1] == '\n';
+ } else {
+ msgoutlen = 0;
+ msgout = estrdup("");
+ }
+ break;
+
+ case P_STDOUT:
+ case P_STDERR:
+ if (msg) {
+ PHPDBG_G(last_was_newline) = msg[msglen - 1] == '\n';
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ if (PHPDBG_G(in_script_xml) != type) {
+ char *stream_buf;
+ int stream_buflen = phpdbg_asprintf(&stream_buf, "<stream type=\"%s\">", type == P_STDERR ? "stderr" : "stdout");
+ phpdbg_mixed_write(fd, stream_buf, stream_buflen TSRMLS_CC);
+ efree(stream_buf);
+ PHPDBG_G(in_script_xml) = type;
+ }
+#if PHP_VERSION_ID >= 50600
+ buf = php_escape_html_entities((unsigned char *) msg, msglen, (size_t *) &buflen, 0, ENT_NOQUOTES, PG(internal_encoding) && PG(internal_encoding)[0] ? PG(internal_encoding) : (SG(default_charset) ? SG(default_charset) : "UTF-8") TSRMLS_CC);
+#else
+ buf = php_escape_html_entities((unsigned char *) msg, msglen, (size_t *) &buflen, 0, ENT_NOQUOTES, SG(default_charset) ? SG(default_charset) : "UTF-8" TSRMLS_CC);
+#endif
+ phpdbg_encode_ctrl_chars(&buf, &buflen);
+ phpdbg_mixed_write(fd, buf, buflen TSRMLS_CC);
+ efree(buf);
+ } else {
+ phpdbg_mixed_write(fd, msg, msglen TSRMLS_CC);
+ }
+ return msglen;
+ }
+ break;
+
+ /* no formatting on logging output */
+ case P_LOG:
+ severity = "log";
+ if (msg) {
+ struct timeval tp;
+ if (gettimeofday(&tp, NULL) == SUCCESS) {
+ msgoutlen = phpdbg_asprintf(&msgout, "[%ld %.8F]: %.*s\n", tp.tv_sec, tp.tv_usec / 1000000., msglen, msg);
+ } else {
+ msgoutlen = FAILURE;
+ }
+ }
+ break;
+ }
+
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ char *xmlout;
+
+ if (PHPDBG_G(req_id)) {
+ char *xmlbuf = NULL;
+ xmllen = phpdbg_asprintf(&xmlbuf, "req=\"%lu\" %.*s", PHPDBG_G(req_id), xmllen, xml);
+ xml = xmlbuf;
+ }
+ if (msgout) {
+ buflen = phpdbg_encode_xml(&buf, msgout, msgoutlen, '"', "&quot;");
+ xmloutlen = phpdbg_asprintf(&xmlout, "<%s severity=\"%s\" %.*s msgout=\"%.*s\" />", tag, severity, xmllen, xml, buflen, buf);
+
+ efree(buf);
+ } else {
+ xmloutlen = phpdbg_asprintf(&xmlout, "<%s severity=\"%s\" %.*s msgout=\"\" />", tag, severity, xmllen, xml);
+ }
+
+ phpdbg_encode_ctrl_chars(&xmlout, &xmloutlen);
+ phpdbg_eol_convert(&xmlout, &xmloutlen TSRMLS_CC);
+ phpdbg_mixed_write(fd, xmlout, xmloutlen TSRMLS_CC);
+ efree(xmlout);
+ } else if (msgout) {
+ phpdbg_eol_convert(&msgout, &msgoutlen TSRMLS_CC);
+ phpdbg_mixed_write(fd, msgout, msgoutlen TSRMLS_CC);
+ }
+
+ if (PHPDBG_G(req_id) && (PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
+ efree((char *) xml);
+ }
+
+ if (msgout) {
+ efree(msgout);
+ }
+
+ return msgout ? msgoutlen : xmloutlen;
+} /* }}} */
+
+PHPDBG_API int phpdbg_vprint(int type TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, va_list args) {
+ char *msg = NULL, *xml = NULL;
+ int msglen = 0, xmllen = 0;
+ int len;
+ va_list argcpy;
+
+ if (strfmt != NULL && strlen(strfmt) > 0L) {
+ va_copy(argcpy, args);
+ msglen = phpdbg_xml_vasprintf(&msg, strfmt, 0, argcpy TSRMLS_CC);
+ va_end(argcpy);
+ }
+
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ if (xmlfmt != NULL && strlen(xmlfmt) > 0L) {
+ va_copy(argcpy, args);
+ xmllen = phpdbg_xml_vasprintf(&xml, xmlfmt, 1, argcpy TSRMLS_CC);
+ va_end(argcpy);
+ } else {
+ xml = estrdup("");
+ }
+ }
+
+ if (PHPDBG_G(err_buf).active && type != P_STDOUT && type != P_STDERR) {
+ PHPDBG_G(err_buf).type = type;
+ PHPDBG_G(err_buf).fd = fd;
+ PHPDBG_G(err_buf).tag = estrdup(tag);
+ PHPDBG_G(err_buf).msg = msg;
+ PHPDBG_G(err_buf).msglen = msglen;
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ PHPDBG_G(err_buf).xml = xml;
+ PHPDBG_G(err_buf).xmllen = xmllen;
+ }
+
+ return msglen;
+ }
+
+ len = phpdbg_process_print(fd, type, tag, msg, msglen, xml, xmllen TSRMLS_CC);
+
+ if (msg) {
+ efree(msg);
+ }
+
+ if (xml) {
+ efree(xml);
+ }
+
+ return len;
+}
+
+PHPDBG_API void phpdbg_free_err_buf(TSRMLS_D) {
+ if (PHPDBG_G(err_buf).type == 0) {
+ return;
+ }
+
+ PHPDBG_G(err_buf).type = 0;
+
+ efree(PHPDBG_G(err_buf).tag);
+ efree(PHPDBG_G(err_buf).msg);
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ efree(PHPDBG_G(err_buf).xml);
+ }
+}
+
+PHPDBG_API void phpdbg_activate_err_buf(zend_bool active TSRMLS_DC) {
+ PHPDBG_G(err_buf).active = active;
+}
+
+PHPDBG_API int phpdbg_output_err_buf(const char *tag, const char *xmlfmt, const char *strfmt TSRMLS_DC, ...) {
+ int len;
+ va_list args;
+ int errbuf_active = PHPDBG_G(err_buf).active;
+
+ PHPDBG_G(err_buf).active = 0;
+
+#ifdef ZTS
+ va_start(args, tsrm_ls);
+#else
+ va_start(args, strfmt);
+#endif
+ len = phpdbg_vprint(PHPDBG_G(err_buf).type TSRMLS_CC, PHPDBG_G(err_buf).fd, tag ? tag : PHPDBG_G(err_buf).tag, xmlfmt, strfmt, args);
+ va_end(args);
+
+ PHPDBG_G(err_buf).active = errbuf_active;
+ phpdbg_free_err_buf(TSRMLS_C);
+
+ return len;
+}
+
+PHPDBG_API int phpdbg_print(int type TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) {
+ va_list args;
+ int len;
+
+ va_start(args, strfmt);
+ len = phpdbg_vprint(type TSRMLS_CC, fd, tag, xmlfmt, strfmt, args);
+ va_end(args);
+
+ return len;
+}
+
+PHPDBG_API int phpdbg_xml_internal(int fd TSRMLS_DC, const char *fmt, ...) {
+ int len = 0;
+
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ va_list args;
+ char *buffer;
+ int buflen;
+
+ va_start(args, fmt);
+ buflen = phpdbg_xml_vasprintf(&buffer, fmt, 1, args TSRMLS_CC);
+ va_end(args);
+
+ phpdbg_encode_ctrl_chars(&buffer, &buflen);
+
+ if (PHPDBG_G(in_script_xml)) {
+ phpdbg_mixed_write(fd, ZEND_STRL("</stream>") TSRMLS_CC);
+ PHPDBG_G(in_script_xml) = 0;
+ }
+
+ len = phpdbg_mixed_write(fd, buffer, buflen TSRMLS_CC);
+ efree(buffer);
+ }
+
+ return len;
+}
+
+PHPDBG_API int phpdbg_log_internal(int fd TSRMLS_DC, const char *fmt, ...) {
+ va_list args;
+ char *buffer;
+ int buflen;
+ int len = 0;
+
+ va_start(args, fmt);
+ buflen = phpdbg_xml_vasprintf(&buffer, fmt, 0, args TSRMLS_CC);
+ va_end(args);
+
+ len = phpdbg_mixed_write(fd, buffer, buflen TSRMLS_CC);
+ efree(buffer);
+
+ return len;
+}
+
+PHPDBG_API int phpdbg_out_internal(int fd TSRMLS_DC, const char *fmt, ...) {
+ va_list args;
+ char *buffer;
+ int buflen;
+ int len = 0;
+
+ va_start(args, fmt);
+ buflen = phpdbg_xml_vasprintf(&buffer, fmt, 0, args TSRMLS_CC);
+ va_end(args);
+
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ char *msg;
+ int msglen;
+
+ msglen = phpdbg_encode_xml(&msg, buffer, buflen, 256, NULL);
+ phpdbg_encode_ctrl_chars(&msg, &msglen);
+ phpdbg_eol_convert(&msg, &msglen TSRMLS_CC);
+
+ if (PHPDBG_G(in_script_xml)) {
+ phpdbg_mixed_write(fd, ZEND_STRL("</stream>") TSRMLS_CC);
+ PHPDBG_G(in_script_xml) = 0;
+ }
+
+ phpdbg_mixed_write(fd, ZEND_STRL("<phpdbg>") TSRMLS_CC);
+ len = phpdbg_mixed_write(fd, msg, msglen TSRMLS_CC);
+ phpdbg_mixed_write(fd, ZEND_STRL("</phpdbg>") TSRMLS_CC);
+ } else {
+ phpdbg_eol_convert(&buffer, &buflen TSRMLS_CC);
+ len = phpdbg_mixed_write(fd, buffer, buflen TSRMLS_CC);
+ }
+
+ return len;
+}
+
+
+PHPDBG_API int phpdbg_rlog_internal(int fd TSRMLS_DC, const char *fmt, ...) { /* {{{ */
+ int rc = 0;
+
+ va_list args;
+ struct timeval tp;
+
+ va_start(args, fmt);
+ if (gettimeofday(&tp, NULL) == SUCCESS) {
+ char friendly[100];
+ char *format = NULL, *buffer = NULL, *outbuf = NULL;
+ const time_t tt = tp.tv_sec;
+
+#ifdef PHP_WIN32
+ strftime(friendly, 100, "%a %b %d %H.%%04d %Y", localtime(&tt));
+#else
+ strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tt));
+#endif
+ phpdbg_asprintf(&buffer, friendly, tp.tv_usec/1000);
+ phpdbg_asprintf(&format, "[%s]: %s\n", buffer, fmt);
+ rc = phpdbg_xml_vasprintf(&outbuf, format, 0, args TSRMLS_CC);
+
+ if (outbuf) {
+ rc = phpdbg_mixed_write(fd, outbuf, rc TSRMLS_CC);
+ efree(outbuf);
+ }
+
+ efree(format);
+ efree(buffer);
+ }
+ va_end(args);
+
+ return rc;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_out.h b/sapi/phpdbg/phpdbg_out.h
new file mode 100644
index 0000000000..ea25b04279
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_out.h
@@ -0,0 +1,93 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_OUT_H
+#define PHPDBG_OUT_H
+
+/**
+ * Error/notice/formatting helpers
+ */
+enum {
+ P_ERROR = 1,
+ P_NOTICE,
+ P_WRITELN,
+ P_WRITE,
+ P_STDOUT,
+ P_STDERR,
+ P_LOG
+};
+
+#ifdef ZTS
+PHPDBG_API int phpdbg_print(int severity TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 6, 7);
+PHPDBG_API int phpdbg_xml_internal(int fd TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
+PHPDBG_API int phpdbg_log_internal(int fd TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
+PHPDBG_API int phpdbg_out_internal(int fd TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
+PHPDBG_API int phpdbg_rlog_internal(int fd TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
+#else
+PHPDBG_API int phpdbg_print(int severity, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 5, 6);
+PHPDBG_API int phpdbg_xml_internal(int fd, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3);
+PHPDBG_API int phpdbg_log_internal(int fd, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3);
+PHPDBG_API int phpdbg_out_internal(int fd, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3);
+PHPDBG_API int phpdbg_rlog_internal(int fd, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3);
+#endif
+
+
+#define phpdbg_error(tag, xmlfmt, strfmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_notice(tag, xmlfmt, strfmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_writeln(tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_write(tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_script(type, fmt, ...) phpdbg_print(type TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, NULL, NULL, fmt, ##__VA_ARGS__)
+#define phpdbg_log(fmt, ...) phpdbg_log_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+#define phpdbg_xml(fmt, ...) phpdbg_xml_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+#define phpdbg_out(fmt, ...) phpdbg_out_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+
+#define phpdbg_error_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_notice_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_writeln_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_write_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
+#define phpdbg_script_ex(out, type, fmt, ...) phpdbg_print(type TSRMLS_CC, out, NULL, NULL, fmt, ##__VA_ARGS__)
+#define phpdbg_log_ex(out, fmt, ...) phpdbg_log_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+#define phpdbg_xml_ex(out, fmt, ...) phpdbg_xml_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+#define phpdbg_out_ex(out, fmt, ...) phpdbg_out_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+
+#define phpdbg_rlog(fd, fmt, ...) phpdbg_rlog_internal(fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+
+#define phpdbg_xml_asprintf(buf, ...) _phpdbg_xml_asprintf(buf TSRMLS_CC, ##__VA_ARGS__)
+PHPDBG_API int _phpdbg_xml_asprintf(char **buf TSRMLS_DC, const char *format, zend_bool escape_xml, ...);
+
+#define phpdbg_asprintf(buf, ...) _phpdbg_asprintf(buf TSRMLS_CC, ##__VA_ARGS__)
+PHPDBG_API int _phpdbg_asprintf(char **buf TSRMLS_DC, const char *format, ...);
+
+
+#if PHPDBG_DEBUG
+# define phpdbg_debug(fmt, ...) phpdbg_log_ex(PHPDBG_G(io)[PHPDBG_STDERR].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
+#else
+# define phpdbg_debug(fmt, ...)
+#endif
+
+PHPDBG_API void phpdbg_free_err_buf(TSRMLS_D);
+PHPDBG_API void phpdbg_activate_err_buf(zend_bool active TSRMLS_DC);
+PHPDBG_API int phpdbg_output_err_buf(const char *tag, const char *xmlfmt, const char *strfmt TSRMLS_DC, ...);
+
+
+/* {{{ For separation */
+#define SEPARATE "------------------------------------------------" /* }}} */
+
+#endif /* PHPDBG_OUT_H */
diff --git a/sapi/phpdbg/phpdbg_parser.c b/sapi/phpdbg/phpdbg_parser.c
new file mode 100644
index 0000000000..c766868369
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.c
@@ -0,0 +1,1951 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse phpdbg_parse
+#define yylex phpdbg_lex
+#define yyerror phpdbg_error
+#define yylval phpdbg_lval
+#define yychar phpdbg_char
+#define yydebug phpdbg_debug
+#define yynerrs phpdbg_nerrs
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 1 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+
+
+/*
+ * 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"
+
+#undef yyerror
+static int yyerror(void ***tsrm_ls, const char *msg);
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+
+
+/* Line 268 of yacc.c */
+#line 107 "sapi/phpdbg/phpdbg_parser.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+/* "%code requires" blocks. */
+
+/* Line 288 of yacc.c */
+#line 31 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+
+
+/* Line 288 of yacc.c */
+#line 141 "sapi/phpdbg/phpdbg_parser.c"
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_EVAL = 258,
+ T_RUN = 259,
+ T_SHELL = 260,
+ T_IF = 261,
+ T_TRUTHY = 262,
+ T_FALSY = 263,
+ T_STRING = 264,
+ T_COLON = 265,
+ T_DCOLON = 266,
+ T_POUND = 267,
+ T_PROTO = 268,
+ T_DIGITS = 269,
+ T_LITERAL = 270,
+ T_ADDR = 271,
+ T_OPCODE = 272,
+ T_ID = 273,
+ T_INPUT = 274,
+ T_UNEXPECTED = 275,
+ T_REQ_ID = 276
+ };
+#endif
+/* Tokens. */
+#define T_EVAL 258
+#define T_RUN 259
+#define T_SHELL 260
+#define T_IF 261
+#define T_TRUTHY 262
+#define T_FALSY 263
+#define T_STRING 264
+#define T_COLON 265
+#define T_DCOLON 266
+#define T_POUND 267
+#define T_PROTO 268
+#define T_DIGITS 269
+#define T_LITERAL 270
+#define T_ADDR 271
+#define T_OPCODE 272
+#define T_ID 273
+#define T_INPUT 274
+#define T_UNEXPECTED 275
+#define T_REQ_ID 276
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 343 of yacc.c */
+#line 206 "sapi/phpdbg/phpdbg_parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 26
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 48
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 22
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 6
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 28
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 43
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 276
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 7, 8, 10, 13, 16, 20,
+ 25, 30, 36, 40, 46, 50, 53, 55, 57, 59,
+ 61, 63, 65, 67, 69, 70, 74, 78, 81
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 23, 0, -1, 24, -1, 27, -1, -1, 25, -1,
+ 24, 25, -1, 24, 26, -1, 18, 10, 14, -1,
+ 18, 10, 12, 14, -1, 13, 18, 10, 14, -1,
+ 13, 18, 10, 12, 14, -1, 18, 11, 18, -1,
+ 18, 11, 18, 12, 14, -1, 18, 12, 14, -1,
+ 6, 19, -1, 17, -1, 16, -1, 15, -1, 7,
+ -1, 8, -1, 14, -1, 18, -1, 21, -1, -1,
+ 3, 26, 19, -1, 5, 26, 19, -1, 4, 26,
+ -1, 4, 26, 19, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 67, 67, 68, 69, 73, 74, 75, 79, 84,
+ 89, 99, 109, 114, 120, 126, 131, 132, 133, 134,
+ 135, 136, 137, 141, 142, 146, 151, 156, 160
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "\"eval\"", "\"run\"", "\"shell\"",
+ "\"if (condition)\"", "\"truthy (true, on, yes or enabled)\"",
+ "\"falsy (false, off, no or disabled)\"",
+ "\"string (some input, perhaps)\"", "\": (colon)\"",
+ "\":: (double colon)\"", "\"# (pound sign)\"", "\"protocol (file://)\"",
+ "\"digits (numbers)\"", "\"literal (string)\"", "\"address\"",
+ "\"opcode\"", "\"identifier (command or function name)\"",
+ "\"input (input string or data)\"", "\"input\"",
+ "\"request id (-r %d)\"", "$accept", "input", "parameters", "parameter",
+ "req_id", "full_expression", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 22, 23, 23, 23, 24, 24, 24, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 26, 26, 27, 27, 27, 27
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 0, 1, 2, 2, 3, 4,
+ 4, 5, 3, 5, 3, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 0, 3, 3, 2, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 4, 24, 24, 24, 0, 19, 20, 0, 21, 18,
+ 17, 16, 22, 0, 2, 5, 3, 23, 0, 27,
+ 0, 15, 0, 0, 0, 0, 1, 6, 7, 25,
+ 28, 26, 0, 0, 8, 12, 14, 0, 10, 9,
+ 0, 11, 13
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 13, 14, 15, 18, 16
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -16
+static const yytype_int8 yypact[] =
+{
+ -3, -15, -15, -15, -10, -16, -16, 3, -16, -16,
+ -16, -16, 22, 29, 10, -16, -16, -16, 11, 17,
+ 19, -16, 30, 8, 21, 27, -16, -16, -16, -16,
+ -16, -16, 23, 28, -16, 31, -16, 32, -16, -16,
+ 33, -16, -16
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -16, -16, -16, 34, 5, -16
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 1, 2, 3, 4, 5, 6, 17, 19, 20, 21,
+ 7, 8, 9, 10, 11, 12, 4, 5, 6, 28,
+ 33, 22, 34, 7, 8, 9, 10, 11, 12, 26,
+ 29, 17, 23, 24, 25, 37, 30, 38, 31, 35,
+ 32, 36, 39, 40, 0, 0, 41, 42, 27
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-16))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 3, 4, 5, 6, 7, 8, 21, 2, 3, 19,
+ 13, 14, 15, 16, 17, 18, 6, 7, 8, 14,
+ 12, 18, 14, 13, 14, 15, 16, 17, 18, 0,
+ 19, 21, 10, 11, 12, 12, 19, 14, 19, 18,
+ 10, 14, 14, 12, -1, -1, 14, 14, 14
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 4, 5, 6, 7, 8, 13, 14, 15,
+ 16, 17, 18, 23, 24, 25, 27, 21, 26, 26,
+ 26, 19, 18, 10, 11, 12, 0, 25, 26, 19,
+ 19, 19, 10, 12, 14, 18, 14, 12, 14, 14,
+ 12, 14, 14
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (tsrm_ls, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, tsrm_ls); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, void *tsrm_ls)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, tsrm_ls)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ void *tsrm_ls;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (tsrm_ls);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, void *tsrm_ls)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, tsrm_ls)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ void *tsrm_ls;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, tsrm_ls);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule, void *tsrm_ls)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule, tsrm_ls)
+ YYSTYPE *yyvsp;
+ int yyrule;
+ void *tsrm_ls;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , tsrm_ls);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule, tsrm_ls); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, void *tsrm_ls)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, tsrm_ls)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ void *tsrm_ls;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (tsrm_ls);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *tsrm_ls);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *tsrm_ls)
+#else
+int
+yyparse (tsrm_ls)
+ void *tsrm_ls;
+#endif
+#endif
+{
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+
+/* Line 1806 of yacc.c */
+#line 68 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { phpdbg_stack_push(PHPDBG_G(parser_stack), &(yyvsp[(1) - (1)])); }
+ break;
+
+ case 5:
+
+/* Line 1806 of yacc.c */
+#line 73 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { phpdbg_stack_push(PHPDBG_G(parser_stack), &(yyvsp[(1) - (1)])); }
+ break;
+
+ case 6:
+
+/* Line 1806 of yacc.c */
+#line 74 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { phpdbg_stack_push(PHPDBG_G(parser_stack), &(yyvsp[(2) - (2)])); }
+ break;
+
+ case 7:
+
+/* Line 1806 of yacc.c */
+#line 75 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (2)]); }
+ break;
+
+ case 8:
+
+/* Line 1806 of yacc.c */
+#line 79 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = FILE_PARAM;
+ (yyval).file.name = (yyvsp[(2) - (3)]).str;
+ (yyval).file.line = (yyvsp[(3) - (3)]).num;
+ }
+ break;
+
+ case 9:
+
+/* Line 1806 of yacc.c */
+#line 84 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_FILE_PARAM;
+ (yyval).file.name = (yyvsp[(1) - (4)]).str;
+ (yyval).file.line = (yyvsp[(4) - (4)]).num;
+ }
+ break;
+
+ case 10:
+
+/* Line 1806 of yacc.c */
+#line 89 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = FILE_PARAM;
+ (yyval).file.name = malloc((yyvsp[(1) - (4)]).len + (yyvsp[(2) - (4)]).len + 1);
+ if ((yyval).file.name) {
+ memcpy(&(yyval).file.name[0], (yyvsp[(1) - (4)]).str, (yyvsp[(1) - (4)]).len);
+ memcpy(&(yyval).file.name[(yyvsp[(1) - (4)]).len], (yyvsp[(2) - (4)]).str, (yyvsp[(2) - (4)]).len);
+ (yyval).file.name[(yyvsp[(1) - (4)]).len + (yyvsp[(2) - (4)]).len] = '\0';
+ }
+ (yyval).file.line = (yyvsp[(4) - (4)]).num;
+ }
+ break;
+
+ case 11:
+
+/* Line 1806 of yacc.c */
+#line 99 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_FILE_PARAM;
+ (yyval).file.name = malloc((yyvsp[(1) - (5)]).len + (yyvsp[(2) - (5)]).len + 1);
+ if ((yyval).file.name) {
+ memcpy(&(yyval).file.name[0], (yyvsp[(1) - (5)]).str, (yyvsp[(1) - (5)]).len);
+ memcpy(&(yyval).file.name[(yyvsp[(1) - (5)]).len], (yyvsp[(2) - (5)]).str, (yyvsp[(2) - (5)]).len);
+ (yyval).file.name[(yyvsp[(1) - (5)]).len + (yyvsp[(2) - (5)]).len] = '\0';
+ }
+ (yyval).file.line = (yyvsp[(5) - (5)]).num;
+ }
+ break;
+
+ case 12:
+
+/* Line 1806 of yacc.c */
+#line 109 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = METHOD_PARAM;
+ (yyval).method.class = (yyvsp[(1) - (3)]).str;
+ (yyval).method.name = (yyvsp[(3) - (3)]).str;
+ }
+ break;
+
+ case 13:
+
+/* Line 1806 of yacc.c */
+#line 114 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_METHOD_PARAM;
+ (yyval).method.class = (yyvsp[(1) - (5)]).str;
+ (yyval).method.name = (yyvsp[(3) - (5)]).str;
+ (yyval).num = (yyvsp[(5) - (5)]).num;
+ }
+ break;
+
+ case 14:
+
+/* Line 1806 of yacc.c */
+#line 120 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_FUNCTION_PARAM;
+ (yyval).str = (yyvsp[(1) - (3)]).str;
+ (yyval).len = (yyvsp[(1) - (3)]).len;
+ (yyval).num = (yyvsp[(3) - (3)]).num;
+ }
+ break;
+
+ case 15:
+
+/* Line 1806 of yacc.c */
+#line 126 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = COND_PARAM;
+ (yyval).str = (yyvsp[(2) - (2)]).str;
+ (yyval).len = (yyvsp[(2) - (2)]).len;
+ }
+ break;
+
+ case 16:
+
+/* Line 1806 of yacc.c */
+#line 131 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 17:
+
+/* Line 1806 of yacc.c */
+#line 132 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 18:
+
+/* Line 1806 of yacc.c */
+#line 133 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 19:
+
+/* Line 1806 of yacc.c */
+#line 134 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 20:
+
+/* Line 1806 of yacc.c */
+#line 135 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 21:
+
+/* Line 1806 of yacc.c */
+#line 136 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 22:
+
+/* Line 1806 of yacc.c */
+#line 137 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 23:
+
+/* Line 1806 of yacc.c */
+#line 141 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ { PHPDBG_G(req_id) = (yyvsp[(1) - (1)]).num; }
+ break;
+
+ case 25:
+
+/* Line 1806 of yacc.c */
+#line 146 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = EVAL_PARAM;
+ (yyval).str = (yyvsp[(3) - (3)]).str;
+ (yyval).len = (yyvsp[(3) - (3)]).len;
+ }
+ break;
+
+ case 26:
+
+/* Line 1806 of yacc.c */
+#line 151 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = SHELL_PARAM;
+ (yyval).str = (yyvsp[(3) - (3)]).str;
+ (yyval).len = (yyvsp[(3) - (3)]).len;
+ }
+ break;
+
+ case 27:
+
+/* Line 1806 of yacc.c */
+#line 156 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = RUN_PARAM;
+ (yyval).len = 0;
+ }
+ break;
+
+ case 28:
+
+/* Line 1806 of yacc.c */
+#line 160 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = RUN_PARAM;
+ (yyval).str = (yyvsp[(3) - (3)]).str;
+ (yyval).len = (yyvsp[(3) - (3)]).len;
+ }
+ break;
+
+
+
+/* Line 1806 of yacc.c */
+#line 1695 "sapi/phpdbg/phpdbg_parser.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (tsrm_ls, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (tsrm_ls, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, tsrm_ls);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, tsrm_ls);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (tsrm_ls, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, tsrm_ls);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, tsrm_ls);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c */
+#line 167 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+
+
+static int yyerror(void ***tsrm_ls, const char *msg) {
+ phpdbg_error("command", "type=\"parseerror\" msg=\"%s\"", "Parse Error: %s", msg);
+
+ {
+ const phpdbg_param_t *top = PHPDBG_G(parser_stack);
+
+ while (top) {
+ phpdbg_param_debug(top, "--> ");
+ top = top->next;
+ }
+ }
+ return 0;
+}
+
+int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC) {
+ phpdbg_init_lexer(stack, input TSRMLS_CC);
+
+#ifdef ZTS
+ return yyparse(TSRMLS_C);
+#else
+ return yyparse(NULL);
+#endif
+}
+
diff --git a/sapi/phpdbg/phpdbg_parser.h b/sapi/phpdbg/phpdbg_parser.h
new file mode 100644
index 0000000000..dfa155d718
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.h
@@ -0,0 +1,109 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* "%code requires" blocks. */
+
+/* Line 2068 of yacc.c */
+#line 31 "/root/php-src-xml-data-phpdbg/sapi/phpdbg/phpdbg_parser.y"
+
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+
+
+/* Line 2068 of yacc.c */
+#line 48 "sapi/phpdbg/phpdbg_parser.h"
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_EVAL = 258,
+ T_RUN = 259,
+ T_SHELL = 260,
+ T_IF = 261,
+ T_TRUTHY = 262,
+ T_FALSY = 263,
+ T_STRING = 264,
+ T_COLON = 265,
+ T_DCOLON = 266,
+ T_POUND = 267,
+ T_PROTO = 268,
+ T_DIGITS = 269,
+ T_LITERAL = 270,
+ T_ADDR = 271,
+ T_OPCODE = 272,
+ T_ID = 273,
+ T_INPUT = 274,
+ T_UNEXPECTED = 275,
+ T_REQ_ID = 276
+ };
+#endif
+/* Tokens. */
+#define T_EVAL 258
+#define T_RUN 259
+#define T_SHELL 260
+#define T_IF 261
+#define T_TRUTHY 262
+#define T_FALSY 263
+#define T_STRING 264
+#define T_COLON 265
+#define T_DCOLON 266
+#define T_POUND 267
+#define T_PROTO 268
+#define T_DIGITS 269
+#define T_LITERAL 270
+#define T_ADDR 271
+#define T_OPCODE 272
+#define T_ID 273
+#define T_INPUT 274
+#define T_UNEXPECTED 275
+#define T_REQ_ID 276
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+
diff --git a/sapi/phpdbg/phpdbg_parser.y b/sapi/phpdbg/phpdbg_parser.y
new file mode 100644
index 0000000000..e4353976f3
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.y
@@ -0,0 +1,191 @@
+%{
+
+/*
+ * 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"
+
+#undef yyerror
+static int yyerror(void ***tsrm_ls, const char *msg);
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+%}
+
+%pure-parser
+%error-verbose
+
+%code requires {
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+}
+
+%parse-param { void *tsrm_ls }
+
+%output "sapi/phpdbg/phpdbg_parser.c"
+%defines "sapi/phpdbg/phpdbg_parser.h"
+
+%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"
+%token T_REQ_ID "request id (-r %d)"
+
+%% /* Rules */
+
+input
+ : parameters
+ | full_expression { phpdbg_stack_push(PHPDBG_G(parser_stack), &$1); }
+ | /* nothing */
+ ;
+
+parameters
+ : parameter { phpdbg_stack_push(PHPDBG_G(parser_stack), &$1); }
+ | parameters parameter { phpdbg_stack_push(PHPDBG_G(parser_stack), &$2); }
+ | parameters req_id { $$ = $1; }
+ ;
+
+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_OPCODE { $$ = $1; }
+ | T_ADDR { $$ = $1; }
+ | T_LITERAL { $$ = $1; }
+ | T_TRUTHY { $$ = $1; }
+ | T_FALSY { $$ = $1; }
+ | T_DIGITS { $$ = $1; }
+ | T_ID { $$ = $1; }
+ ;
+
+req_id
+ : T_REQ_ID { PHPDBG_G(req_id) = $1.num; }
+ | /* empty */
+;
+
+full_expression
+ : T_EVAL req_id T_INPUT {
+ $$.type = EVAL_PARAM;
+ $$.str = $3.str;
+ $$.len = $3.len;
+ }
+ | T_SHELL req_id T_INPUT {
+ $$.type = SHELL_PARAM;
+ $$.str = $3.str;
+ $$.len = $3.len;
+ }
+ | T_RUN req_id {
+ $$.type = RUN_PARAM;
+ $$.len = 0;
+ }
+ | T_RUN req_id T_INPUT {
+ $$.type = RUN_PARAM;
+ $$.str = $3.str;
+ $$.len = $3.len;
+ }
+ ;
+
+%%
+
+static int yyerror(void ***tsrm_ls, const char *msg) {
+ phpdbg_error("command", "type=\"parseerror\" msg=\"%s\"", "Parse Error: %s", msg);
+
+ {
+ const phpdbg_param_t *top = PHPDBG_G(parser_stack);
+
+ while (top) {
+ phpdbg_param_debug(top, "--> ");
+ top = top->next;
+ }
+ }
+ return 0;
+}
+
+int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC) {
+ phpdbg_init_lexer(stack, input TSRMLS_CC);
+
+#ifdef ZTS
+ return yyparse(TSRMLS_C);
+#else
+ return yyparse(NULL);
+#endif
+}
diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c
new file mode 100644
index 0000000000..80eeddb39d
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_print.c
@@ -0,0 +1,264 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_print.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_prompt.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s, flags) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9], flags)
+
+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_ASYNC_SAFE),
+ PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m", PHPDBG_ASYNC_SAFE),
+ PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_END_COMMAND
+};
+
+PHPDBG_PRINT(opline) /* {{{ */
+{
+ if (EG(in_execution) && EG(current_execute_data)) {
+ phpdbg_print_opline(EG(current_execute_data), 1 TSRMLS_CC);
+ } else {
+ phpdbg_error("inactive", "type=\"execution\"", "Not Executing!");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC) /* {{{ */
+{
+ switch (method->type) {
+ case ZEND_USER_FUNCTION: {
+ zend_op_array* op_array = &(method->op_array);
+ HashTable vars;
+
+ if (op_array) {
+ zend_op *opline = &(op_array->opcodes[0]);
+ zend_uint opcode = 0,
+ end = op_array->last-1;
+
+ if (method->common.scope) {
+ phpdbg_writeln("printoplineinfo", "type=\"User\" startline=\"%d\" endline=\"%d\" method=\"%s::%s\" file=\"%s\"", "\tL%d-%d %s::%s() %s",
+ op_array->line_start,
+ op_array->line_end,
+ method->common.scope->name,
+ method->common.function_name,
+ op_array->filename ? op_array->filename : "unknown");
+ } else {
+ phpdbg_writeln("printoplineinfo", "type=\"User\" startline=\"%d\" endline=\"%d\" function=\"%s\" file=\"%s\"", "\tL%d-%d %s() %s",
+ method->common.function_name ? op_array->line_start : 0,
+ method->common.function_name ? op_array->line_end : 0,
+ method->common.function_name ? method->common.function_name : "{main}",
+ op_array->filename ? op_array->filename : "unknown");
+ }
+
+ zend_hash_init(&vars, op_array->last, NULL, NULL, 0);
+ do {
+ char *decode = phpdbg_decode_opline(op_array, opline, &vars TSRMLS_CC);
+ if (decode != NULL) {
+ phpdbg_writeln("print", "line=\"%u\" opline=\"%p\" opcode=\"%s\" op=\"%s\"", "\t\tL%u\t%p %-30s %s",
+ opline->lineno,
+ opline,
+ phpdbg_decode_opcode(opline->opcode),
+ decode);
+ free(decode);
+ } else {
+ phpdbg_error("print", "type=\"decodefailure\" opline=\"%16p\"", "\tFailed to decode opline %16p", opline);
+ }
+ opline++;
+ } while (opcode++ < end);
+ zend_hash_destroy(&vars);
+ }
+ } break;
+
+ default: {
+ if (method->common.scope) {
+ phpdbg_writeln("printoplineinfo", "type=\"Internal\" method=\"%s::%s\"", "Internal %s::%s()", method->common.scope->name, method->common.function_name);
+ } else {
+ phpdbg_writeln("printoplineinfo", "type=\"Internal\" function=\"%s\"", "\tInternal %s()", method->common.function_name);
+ }
+ }
+ }
+} /* }}} */
+
+PHPDBG_PRINT(exec) /* {{{ */
+{
+ if (PHPDBG_G(exec)) {
+ if (!PHPDBG_G(ops) && !(PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER)) {
+ phpdbg_compile(TSRMLS_C);
+ }
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_notice("printinfo", "file=\"%s\" num=\"%d\"", "Context %s (%d ops)", PHPDBG_G(exec), PHPDBG_G(ops)->last);
+
+ phpdbg_print_function_helper((zend_function*) PHPDBG_G(ops) TSRMLS_CC);
+ }
+ } else {
+ phpdbg_error("inactive", "type=\"nocontext\"", "No execution context set");
+ }
+
+return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(stack) /* {{{ */
+{
+ zend_op_array *ops = EG(active_op_array);
+
+ if (EG(in_execution) && ops) {
+ if (ops->function_name) {
+ if (ops->scope) {
+ phpdbg_notice("printinfo", "method=\"%s::%s\" num=\"%d\"", "Stack in %s::%s() (%d ops)", ops->scope->name, ops->function_name, ops->last);
+ } else {
+ phpdbg_notice("printinfo", "function=\"%s\" num=\"%d\"", "Stack in %s() (%d ops)", ops->function_name, ops->last);
+ }
+ } else {
+ if (ops->filename) {
+ phpdbg_notice("printinfo", "file=\"%s\" num=\"%d\"", "Stack in %s (%d ops)", ops->filename, ops->last);
+ } else {
+ phpdbg_notice("printinfo", "opline=\"%p\" num=\"%d\"", "Stack @ %p (%d ops)", ops, ops->last);
+ }
+ }
+ phpdbg_print_function_helper((zend_function*) ops TSRMLS_CC);
+ } else {
+ phpdbg_error("inactive", "type=\"execution\"", "Not Executing!");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(class) /* {{{ */
+{
+ zend_class_entry **ce;
+
+ if (phpdbg_safe_class_lookup(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ phpdbg_notice("printinfo", "type=\"%s\" flag=\"%s\" class=\"%s\" num=\"%d\"", "%s %s: %s (%d methods)",
+ ((*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,
+ zend_hash_num_elements(&(*ce)->function_table));
+
+ phpdbg_xml("<printmethods %r>");
+
+ 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);
+ }
+ }
+
+ phpdbg_xml("</printmethods>");
+ } else {
+ phpdbg_error("print", "type=\"noclass\" class=\"%s\"", "The class %s could not be found", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(method) /* {{{ */
+{
+ zend_class_entry **ce;
+
+ if (phpdbg_safe_class_lookup(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));
+
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("printinfo", "type=\"%s\" flags=\"Method\" symbol=\"%s\" num=\"%d\"", "%s Method %s (%d ops)",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ fbc->common.function_name,
+ (fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
+
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("print", "type=\"nomethod\" method=\"%s::%s\"", "The method %s::%s could not be found", param->method.class, param->method.name);
+ }
+
+ efree(lcname);
+ } else {
+ phpdbg_error("print", "type=\"noclass\" class=\"%s\"", "The class %s could not be found", param->method.class);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_PRINT(func) /* {{{ */
+{
+ 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("inactive", "type=\"noclasses\"", "No active class");
+ return SUCCESS;
+ }
+ } else if (!EG(function_table)) {
+ phpdbg_error("inactive", "type=\"function_table\"", "No function table loaded");
+ return SUCCESS;
+ } else {
+ func_table = EG(function_table);
+ }
+
+ lcname = zend_str_tolower_dup(func_name, func_name_len);
+
+ phpdbg_try_access {
+ if (zend_hash_find(func_table, lcname, func_name_len + 1, (void **) &fbc) == SUCCESS) {
+ phpdbg_notice("printinfo", "type=\"%s\" flags=\"%s\" symbol=\"%s\" num=\"%d\"", "%s %s %s (%d ops)",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ (fbc->common.scope) ? "Method" : "Function",
+ fbc->common.function_name,
+ (fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
+
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("print", "type=\"nofunction\" function=\"%s\"", "The function %s could not be found", func_name);
+ }
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "function=\"%.*s\"", "Couldn't fetch function %.*s, invalid data source", (int) func_name_len, func_name);
+ } phpdbg_end_try_access();
+
+ efree(lcname);
+
+ return SUCCESS;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_print.h b/sapi/phpdbg/phpdbg_print.h
new file mode 100644
index 0000000000..ed85e965c1
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_print.h
@@ -0,0 +1,40 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_PRINT_H
+#define PHPDBG_PRINT_H
+
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_PRINT(name) PHPDBG_COMMAND(print_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_PRINT(exec);
+PHPDBG_PRINT(opline);
+PHPDBG_PRINT(class);
+PHPDBG_PRINT(method);
+PHPDBG_PRINT(func);
+PHPDBG_PRINT(stack);
+
+extern const phpdbg_command_t phpdbg_print_commands[];
+
+#endif /* PHPDBG_PRINT_H */
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
new file mode 100644
index 0000000000..2e0c698f05
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -0,0 +1,1617 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "zend.h"
+#include "zend_compile.h"
+#include "phpdbg.h"
+
+#include "phpdbg_help.h"
+#include "phpdbg_print.h"
+#include "phpdbg_info.h"
+#include "phpdbg_break.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_list.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_set.h"
+#include "phpdbg_frame.h"
+#include "phpdbg_lexer.h"
+#include "phpdbg_parser.h"
+#include "phpdbg_wait.h"
+#include "phpdbg_eol.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+ZEND_EXTERN_MODULE_GLOBALS(output);
+
+#ifdef HAVE_LIBDL
+#ifdef PHP_WIN32
+#include "win32/param.h"
+#include "win32/winutil.h"
+#define GET_DL_ERROR() php_win_err()
+#elif defined(NETWARE)
+#include <sys/param.h>
+#define GET_DL_ERROR() dlerror()
+#else
+#include <sys/param.h>
+#define GET_DL_ERROR() DL_ERROR()
+#endif
+#endif
+
+/* {{{ command declarations */
+const phpdbg_command_t phpdbg_prompt_commands[] = {
+ PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s", 0),
+ PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s", 0),
+ PHPDBG_COMMAND_D(ev, "evaluate some code", 0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */
+ PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0, 0),
+ PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0, 0),
+ PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0, 0),
+ PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0, 0),
+ PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c", 0),
+ PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0, 0),
+ PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0, 0),
+ PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s", 0),
+ PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s", 0),
+ PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(sh, "shell a command", 0 , NULL, "i", 0),
+ PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(wait, "wait for other process", 'W', NULL, 0, 0),
+ PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss", 0),
+ PHPDBG_COMMAND_D(eol, "set EOL", 'E', NULL, "|s", 0),
+ PHPDBG_END_COMMAND
+}; /* }}} */
+
+static inline int phpdbg_call_register(phpdbg_param_t *stack TSRMLS_DC) /* {{{ */
+{
+ phpdbg_param_t *name = NULL;
+
+ if (stack->type == STACK_PARAM) {
+ char *lc_name;
+
+ name = stack->next;
+
+ if (!name || name->type != STR_PARAM) {
+ return FAILURE;
+ }
+
+ lc_name = zend_str_tolower_dup(name->str, name->len);
+
+ if (zend_hash_exists(&PHPDBG_G(registered), lc_name, name->len+1)) {
+ zval fname, *fretval;
+ zend_fcall_info fci;
+
+ ZVAL_STRINGL(&fname, lc_name, name->len, 1);
+
+ memset(&fci, 0, sizeof(zend_fcall_info));
+
+ fci.size = sizeof(zend_fcall_info);
+ fci.function_table = &PHPDBG_G(registered);
+ fci.function_name = &fname;
+ fci.symbol_table = EG(active_symbol_table);
+ fci.object_ptr = NULL;
+ fci.retval_ptr_ptr = &fretval;
+ fci.no_separation = 1;
+
+ if (name->next) {
+ zval params;
+ phpdbg_param_t *next = name->next;
+
+ array_init(&params);
+
+ while (next) {
+ char *buffered = NULL;
+
+ switch (next->type) {
+ case OP_PARAM:
+ case COND_PARAM:
+ case STR_PARAM:
+ add_next_index_stringl(&params, next->str, next->len, 1);
+ break;
+
+ case NUMERIC_PARAM:
+ add_next_index_long(&params, next->num);
+ break;
+
+ case METHOD_PARAM:
+ spprintf(&buffered, 0, "%s::%s",
+ next->method.class, next->method.name);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ spprintf(&buffered, 0, "%s::%s#%ld",
+ next->method.class, next->method.name, next->num);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ spprintf(&buffered, 0, "%s#%ld",
+ next->str, next->num);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case FILE_PARAM:
+ spprintf(&buffered, 0, "%s:%ld",
+ next->file.name, next->file.line);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ spprintf(&buffered, 0, "%s:#%ld",
+ next->file.name, next->file.line);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ default: {
+ /* not yet */
+ }
+ }
+
+ next = next->next;
+ }
+
+ zend_fcall_info_args(&fci, &params TSRMLS_CC);
+ } else {
+ fci.params = NULL;
+ fci.param_count = 0;
+ }
+
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+
+ phpdbg_debug("created %d params from arguments", fci.param_count);
+
+ zend_call_function(&fci, NULL TSRMLS_CC);
+
+ if (fretval) {
+ zend_print_zval_r(fretval, 0 TSRMLS_CC);
+ phpdbg_out("\n");
+ }
+
+ zval_dtor(&fname);
+ efree(lc_name);
+
+ return SUCCESS;
+ }
+
+ efree(lc_name);
+ }
+
+ return FAILURE;
+} /* }}} */
+
+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) {
+ int line = 1;
+
+ char cmd[PHPDBG_MAX_CMD];
+ size_t cmd_len = 0L;
+ char *code = NULL;
+ size_t code_len = 0L;
+ zend_bool in_code = 0;
+
+ while (fgets(cmd, PHPDBG_MAX_CMD, fp) != NULL) {
+ cmd_len = strlen(cmd)-1;
+
+ while (cmd_len > 0L && isspace(cmd[cmd_len-1]))
+ cmd_len--;
+
+ cmd[cmd_len] = '\0';
+
+ if (*cmd && cmd_len > 0L && cmd[0] != '#') {
+ if (cmd_len == 2) {
+ if (memcmp(cmd, "<:", sizeof("<:")-1) == SUCCESS) {
+ in_code = 1;
+ goto next_line;
+ } else {
+ if (memcmp(cmd, ":>", sizeof(":>")-1) == SUCCESS) {
+ in_code = 0;
+ code[code_len] = '\0';
+ {
+ zend_eval_stringl(code, code_len, NULL, "phpdbginit code" TSRMLS_CC);
+ }
+ free(code);
+ code = NULL;
+ goto next_line;
+ }
+ }
+ }
+
+ if (in_code) {
+ if (code == NULL) {
+ code = malloc(cmd_len + 1);
+ } else code = realloc(code, code_len + cmd_len + 1);
+
+ if (code) {
+ memcpy(
+ &code[code_len], cmd, cmd_len);
+ code_len += cmd_len;
+ }
+ goto next_line;
+ }
+
+ {
+ char *input = phpdbg_read_input(cmd TSRMLS_CC);
+ phpdbg_param_t stack;
+
+ phpdbg_init_param(&stack, STACK_PARAM);
+
+ phpdbg_activate_err_buf(1 TSRMLS_CC);
+
+ if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) {
+ switch (phpdbg_stack_execute(&stack, 1 /* allow_async_unsafe == 1 */ TSRMLS_CC)) {
+ case FAILURE:
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
+ phpdbg_output_err_buf("initfailure", "%b file=\"%s\" line=\"%d\" input=\"%s\"", "Unrecognized command in %s:%d: %s, %b!" TSRMLS_CC, init_file, line, input);
+ }
+ break;
+ }
+ }
+
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ }
+ }
+next_line:
+ line++;
+ }
+
+ if (code) {
+ free(code);
+ }
+
+ fclose(fp);
+ } else {
+ phpdbg_error("initfailure", "type=\"openfile\" file=\"%s\"", "Failed to open %s for initialization", init_file);
+ }
+
+ if (free_init) {
+ free(init_file);
+ }
+ }
+} /* }}} */
+
+void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC) /* {{{ */
+{
+ if (!init_file && use_default) {
+ char *scan_dir = getenv("PHP_INI_SCAN_DIR");
+ int i;
+
+ phpdbg_try_file_init(PHPDBG_STRL(PHP_CONFIG_FILE_PATH "/" PHPDBG_INIT_FILENAME), 0 TSRMLS_CC);
+
+ if (!scan_dir) {
+ scan_dir = PHP_CONFIG_FILE_SCAN_DIR;
+ }
+ while (*scan_dir != 0) {
+ i = 0;
+ while (scan_dir[i] != ':') {
+ if (scan_dir[i++] == 0) {
+ i = -1;
+ break;
+ }
+ }
+ if (i != -1) {
+ scan_dir[i] = 0;
+ }
+
+ asprintf(&init_file, "%s/%s", scan_dir, PHPDBG_INIT_FILENAME);
+ phpdbg_try_file_init(init_file, strlen(init_file), 1 TSRMLS_CC);
+ if (i == -1) {
+ break;
+ }
+ scan_dir += i + 1;
+ }
+
+ phpdbg_try_file_init(PHPDBG_STRL(PHPDBG_INIT_FILENAME), 0 TSRMLS_CC);
+ } else {
+ phpdbg_try_file_init(init_file, init_file_len, 1 TSRMLS_CC);
+ }
+}
+
+PHPDBG_COMMAND(exec) /* {{{ */
+{
+ 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("exec", "type=\"unset\" context=\"%s\"", "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("exec", "type=\"unsetops\"", "Destroying compiled opcodes");
+ phpdbg_clean(0 TSRMLS_CC);
+ }
+
+ PHPDBG_G(exec) = res;
+ PHPDBG_G(exec_len) = res_len;
+
+ VCWD_CHDIR_FILE(res);
+
+ *SG(request_info).argv = PHPDBG_G(exec);
+ php_hash_environment(TSRMLS_C);
+
+ phpdbg_notice("exec", "type=\"set\" context=\"%s\"", "Set execution context: %s", PHPDBG_G(exec));
+
+ if (phpdbg_compile(TSRMLS_C) == FAILURE) {
+ phpdbg_error("compile", "type=\"compilefailure\" context=\"%s\"", "Failed to compile %s", PHPDBG_G(exec));
+ }
+ } else {
+ phpdbg_notice("exec", "type=\"unchanged\"", "Execution context not changed");
+ }
+ } else {
+ phpdbg_error("exec", "type=\"invalid\" context=\"%s\"", "Cannot use %s as execution context, not a valid file or symlink", param->str);
+ }
+ } else {
+ phpdbg_error("exec", "type=\"notfound\" context=\"%s\"", "Cannot stat %s, ensure the file exists", param->str);
+ }
+ return SUCCESS;
+} /* }}} */
+
+int phpdbg_compile(TSRMLS_D) /* {{{ */
+{
+ zend_file_handle fh;
+
+ if (!PHPDBG_G(exec)) {
+ phpdbg_error("inactive", "type=\"nocontext\"", "No execution context");
+ return SUCCESS;
+ }
+
+ if (EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"isrunning\"", "Cannot compile while in execution");
+ return FAILURE;
+ }
+
+ if (php_stream_open_for_zend_ex(PHPDBG_G(exec), &fh, USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC) == SUCCESS) {
+
+ PHPDBG_G(ops) = zend_compile_file(&fh, ZEND_INCLUDE TSRMLS_CC);
+ zend_destroy_file_handle(&fh TSRMLS_CC);
+
+ phpdbg_notice("compile", "context=\"%s\"", "Successful compilation of %s", PHPDBG_G(exec));
+ return SUCCESS;
+ } else {
+ phpdbg_error("compile", "type=\"openfailure\" context=\"%s\"", "Could not open file %s", PHPDBG_G(exec));
+ }
+
+ return FAILURE;
+} /* }}} */
+
+PHPDBG_COMMAND(step) /* {{{ */
+{
+ if (EG(in_execution)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
+ }
+
+ return PHPDBG_NEXT;
+} /* }}} */
+
+PHPDBG_COMMAND(continue) /* {{{ */
+{
+ return PHPDBG_NEXT;
+} /* }}} */
+
+PHPDBG_COMMAND(until) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"noexec\"", "Not executing");
+ return SUCCESS;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_IN_UNTIL;
+ {
+ zend_uint next = 0, self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes);
+ zend_op *opline = &EG(active_op_array)->opcodes[self];
+
+ for (next = self; next < EG(active_op_array)->last; next++) {
+ if (EG(active_op_array)->opcodes[next].lineno != opline->lineno) {
+ zend_hash_index_update(&PHPDBG_G(seek), (zend_ulong) &EG(active_op_array)->opcodes[next], &EG(active_op_array)->opcodes[next], sizeof(zend_op), NULL);
+ break;
+ }
+ }
+ }
+
+ return PHPDBG_UNTIL;
+} /* }}} */
+
+PHPDBG_COMMAND(finish) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"noexec\"", "Not executing");
+ return SUCCESS;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_IN_FINISH;
+ {
+ zend_uint next = 0, self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes);
+
+ for (next = self; next < EG(active_op_array)->last; next++) {
+ switch (EG(active_op_array)->opcodes[next].opcode) {
+ case ZEND_RETURN:
+ case ZEND_THROW:
+ case ZEND_EXIT:
+#ifdef ZEND_YIELD
+ case ZEND_YIELD:
+#endif
+ zend_hash_index_update(&PHPDBG_G(seek), (zend_ulong) &EG(active_op_array)->opcodes[next], &EG(active_op_array)->opcodes[next], sizeof(zend_op), NULL);
+ break;
+ }
+ }
+ }
+
+ return PHPDBG_FINISH;
+} /* }}} */
+
+PHPDBG_COMMAND(leave) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"noexec\"", "Not executing");
+ return SUCCESS;
+ }
+
+ PHPDBG_G(flags) |= PHPDBG_IN_LEAVE;
+ {
+ zend_uint next = 0, self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes);
+
+ for (next = self; next < EG(active_op_array)->last; next++) {
+ switch (EG(active_op_array)->opcodes[next].opcode) {
+ case ZEND_RETURN:
+ case ZEND_THROW:
+ case ZEND_EXIT:
+#ifdef ZEND_YIELD
+ case ZEND_YIELD:
+#endif
+ zend_hash_index_update(&PHPDBG_G(seek), (zend_ulong) &EG(active_op_array)->opcodes[next], &EG(active_op_array)->opcodes[next], sizeof(zend_op), NULL);
+ break;
+ }
+ }
+ }
+
+ return PHPDBG_LEAVE;
+} /* }}} */
+
+PHPDBG_COMMAND(frame) /* {{{ */
+{
+ if (!param) {
+ phpdbg_notice("frame", "id=\"%d\"", "Currently in frame #%d", PHPDBG_G(frame).num);
+ } else {
+ phpdbg_switch_frame(param->num TSRMLS_CC);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static inline void phpdbg_handle_exception(TSRMLS_D) /* }}} */
+{
+ zend_fcall_info fci;
+
+ zval fname, *trace, exception;
+
+ /* get filename and linenumber before unsetting exception */
+ const char *filename = zend_get_executed_filename(TSRMLS_C);
+ zend_uint lineno = zend_get_executed_lineno(TSRMLS_C);
+
+ /* copy exception */
+ exception = *EG(exception);
+ zval_copy_ctor(&exception);
+ EG(exception) = NULL;
+
+ /* call __toString */
+ ZVAL_STRINGL(&fname, "__tostring", sizeof("__tostring")-1, 1);
+ fci.size = sizeof(fci);
+ fci.function_table = &Z_OBJCE(exception)->function_table;
+ fci.function_name = &fname;
+ fci.symbol_table = NULL;
+ fci.object_ptr = &exception;
+ fci.retval_ptr_ptr = &trace;
+ fci.param_count = 0;
+ fci.params = NULL;
+ fci.no_separation = 1;
+ zend_call_function(&fci, NULL TSRMLS_CC);
+
+ if (trace) {
+ phpdbg_writeln("exception", "name=\"%s\" trace=\"%.*s\"", "Uncaught %s!\n%.*s", Z_OBJCE(exception)->name, Z_STRLEN_P(trace), Z_STRVAL_P(trace));
+
+ zval_ptr_dtor(&trace);
+ } else {
+ phpdbg_error("exception", "name=\"%s\"", "Uncaught %s!", Z_OBJCE(exception)->name);
+ }
+
+ /* output useful information about address */
+ phpdbg_writeln("exception", "opline=\"%p\" file=\"%s\" line=\"%u\"", "Stack entered at %p in %s on line %u", EG(active_op_array)->opcodes, filename, lineno);
+
+ zval_dtor(&fname);
+ zval_dtor(&exception);
+} /* }}} */
+
+PHPDBG_COMMAND(run) /* {{{ */
+{
+ if (EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"isrunning\"", "Cannot start another execution while one is in progress");
+ return SUCCESS;
+ }
+
+ if (PHPDBG_G(ops) || PHPDBG_G(exec)) {
+ zend_op **orig_opline = EG(opline_ptr);
+ zend_op_array *orig_op_array = EG(active_op_array);
+ zval **orig_retval_ptr = EG(return_value_ptr_ptr);
+ zend_bool restore = 1;
+ zend_execute_data *ex = EG(current_execute_data);
+
+ if (!PHPDBG_G(ops)) {
+ if (phpdbg_compile(TSRMLS_C) == FAILURE) {
+ phpdbg_error("compile", "type=\"compilefailure\" context=\"%s\"", "Failed to compile %s, cannot run", PHPDBG_G(exec));
+ goto out;
+ }
+ }
+
+ EG(active_op_array) = PHPDBG_G(ops);
+ EG(return_value_ptr_ptr) = &PHPDBG_G(retval);
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+ }
+
+ /* clean up from last execution */
+ if (ex && ex->symbol_table) {
+ zend_hash_clean(ex->symbol_table);
+ }
+
+ /* clean seek state */
+ PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK;
+ zend_hash_clean(
+ &PHPDBG_G(seek));
+
+ /* 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 {
+ PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
+ zend_execute(EG(active_op_array) TSRMLS_CC);
+ PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
+ phpdbg_notice("stop", "type=\"normal\"", "Script ended normally");
+ } zend_catch {
+ EG(active_op_array) = orig_op_array;
+ EG(opline_ptr) = orig_opline;
+ EG(return_value_ptr_ptr) = orig_retval_ptr;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ phpdbg_error("stop", "type=\"bailout\"", "Caught exit/error from VM");
+ restore = 0;
+ }
+ } zend_end_try();
+
+ if (PHPDBG_G(socket_fd) != -1) {
+ close(PHPDBG_G(socket_fd));
+ PHPDBG_G(socket_fd) = -1;
+ }
+
+ if (restore) {
+ if (EG(exception)) {
+ phpdbg_handle_exception(TSRMLS_C);
+ }
+
+ EG(active_op_array) = orig_op_array;
+ EG(opline_ptr) = orig_opline;
+ EG(return_value_ptr_ptr) = orig_retval_ptr;
+ }
+ } else {
+ phpdbg_error("inactive", "type=\"nocontext\"", "Nothing to execute!");
+ }
+
+out:
+ PHPDBG_FRAME(num) = 0;
+ return SUCCESS;
+} /* }}} */
+
+int phpdbg_output_ev_variable(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv TSRMLS_DC) {
+ phpdbg_notice("eval", "variable=\"%.*s\"", "Printing variable %.*s", (int) len, name);
+ phpdbg_xml("<eval %r>");
+ zend_print_zval_r(*zv, 0 TSRMLS_CC);
+ phpdbg_xml("</eval>");
+ phpdbg_out("\n");
+
+ efree(name);
+ efree(keyname);
+
+ return SUCCESS;
+}
+
+PHPDBG_COMMAND(ev) /* {{{ */
+{
+ zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING) == PHPDBG_IS_STEPPING);
+ zval retval;
+
+ zend_op **orig_opline = EG(opline_ptr);
+ zend_op_array *orig_op_array = EG(active_op_array);
+ zval **orig_retval_ptr = EG(return_value_ptr_ptr);
+ zend_execute_data *ex = EG(current_execute_data);
+ HashTable *original_active_symbol_table = EG(active_symbol_table);
+ zval *original_This = EG(This);
+ zend_class_entry *original_scope = EG(scope);
+ zend_class_entry *original_called_scope = EG(called_scope);
+ zend_vm_stack original_stack = EG(argument_stack);
+
+ PHPDBG_OUTPUT_BACKUP();
+
+ if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
+ phpdbg_try_access {
+ phpdbg_parse_variable(param->str, param->len, &EG(symbol_table), 0, phpdbg_output_ev_variable, 0 TSRMLS_CC);
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "", "Could not fetch data, invalid data source");
+ } phpdbg_end_try_access();
+
+ PHPDBG_OUTPUT_BACKUP_RESTORE();
+ return SUCCESS;
+ }
+
+ 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) {
+ phpdbg_xml("<eval %r>");
+ if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
+ zval *zvp = &retval;
+ phpdbg_xml_var_dump(&zvp TSRMLS_CC);
+ }
+ zend_print_zval_r(&retval, 0 TSRMLS_CC);
+ phpdbg_xml("</eval>");
+ phpdbg_out("\n");
+ zval_dtor(&retval);
+ }
+ } zend_catch {
+ EG(active_op_array) = orig_op_array;
+ EG(opline_ptr) = orig_opline;
+ EG(return_value_ptr_ptr) = orig_retval_ptr;
+ EG(current_execute_data) = ex;
+ EG(active_symbol_table) = original_active_symbol_table;
+ EG(This) = original_This;
+ EG(scope) = original_scope;
+ EG(called_scope) = original_called_scope;
+ EG(argument_stack) = original_stack;
+ } zend_end_try();
+ PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL;
+
+ /* switch stepping back on */
+ if (stepping && !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
+ }
+
+ CG(unclean_shutdown) = 0;
+
+ PHPDBG_OUTPUT_BACKUP_RESTORE();
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(back) /* {{{ */
+{
+ if (!EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"noexec\"", "Not executing!");
+ return SUCCESS;
+ }
+
+ if (!param) {
+ phpdbg_dump_backtrace(0 TSRMLS_CC);
+ } else {
+ phpdbg_dump_backtrace(param->num TSRMLS_CC);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(print) /* {{{ */
+{
+ phpdbg_out("Execution Context Information\n\n");
+ phpdbg_xml("<printinfo %r>");
+#ifdef HAVE_LIBREADLINE
+ phpdbg_writeln("print", "readline=\"yes\"", "Readline yes");
+#else
+ phpdbg_writeln("print", "readline=\"no\"", "Readline no");
+#endif
+#ifdef HAVE_LIBEDIT
+ phpdbg_writeln("print", "libedit=\"yes\"", "Libedit yes");
+#else
+ phpdbg_writeln("print", "libedit=\"no\"", "Libedit no");
+#endif
+
+ phpdbg_writeln("print", "context=\"%s\"", "Exec %s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none");
+ phpdbg_writeln("print", "compiled=\"%s\"", "Compiled %s", PHPDBG_G(ops) ? "yes" : "no");
+ phpdbg_writeln("print", "stepping=\"%s\"", "Stepping %s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
+ phpdbg_writeln("print", "quiet=\"%s\"", "Quietness %s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off");
+ phpdbg_writeln("print", "oplog=\"%s\"", "Oplog %s", PHPDBG_G(oplog) ? "on" : "off");
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_writeln("print", "ops=\"%d\"", "Opcodes %d", PHPDBG_G(ops)->last);
+ phpdbg_writeln("print", "vars=\"%d\"", "Variables %d", PHPDBG_G(ops)->last_var ? PHPDBG_G(ops)->last_var - 1 : 0);
+ }
+
+ phpdbg_writeln("print", "executing=\"%d\"", "Executing %s", EG(in_execution) ? "yes" : "no");
+ if (EG(in_execution)) {
+ phpdbg_writeln("print", "vmret=\"%d\"", "VM Return %d", PHPDBG_G(vmret));
+ }
+
+ phpdbg_writeln("print", "classes=\"%d\"", "Classes %d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("print", "functions=\"%d\"", "Functions %d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("print", "constants=\"%d\"", "Constants %d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("print", "includes=\"%d\"", "Included %d", zend_hash_num_elements(&EG(included_files)));
+ phpdbg_xml("</printinfo>");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(info) /* {{{ */
+{
+ phpdbg_error("info", "type=\"toofewargs\" expected=\"1\"", "No information command selected!");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(set) /* {{{ */
+{
+ phpdbg_error("set", "type=\"toofewargs\" expected=\"1\"", "No set command selected!");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(break) /* {{{ */
+{
+ if (!param) {
+ phpdbg_set_breakpoint_file(
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
+ } else switch (param->type) {
+ case ADDR_PARAM:
+ phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
+ break;
+ case NUMERIC_PARAM:
+ if (PHPDBG_G(exec)) {
+ phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
+ } else {
+ phpdbg_error("inactive", "type=\"noexec\"", "Execution context not set!");
+ }
+ break;
+ case METHOD_PARAM:
+ phpdbg_set_breakpoint_method(param->method.class, param->method.name 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;
+ 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();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(sh) /* {{{ */
+{
+ FILE *fd = NULL;
+ if ((fd=VCWD_POPEN((char*)param->str, "w"))) {
+ /* TODO: do something perhaps ?? do we want input ?? */
+ fclose(fd);
+ } else {
+ phpdbg_error("sh", "type=\"failure\" smd=\"%s\"", "Failed to execute %s", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static int add_module_info(zend_module_entry *module TSRMLS_DC) {
+ phpdbg_write("module", "name=\"%s\"", "%s\n", module->name);
+ return 0;
+}
+
+static int add_zendext_info(zend_extension *ext TSRMLS_DC) {
+ phpdbg_write("extension", "name=\"%s\"", "%s\n", ext->name);
+ return 0;
+}
+
+PHPDBG_API const char *phpdbg_load_module_or_extension(char **path, char **name TSRMLS_DC) {
+ DL_HANDLE handle;
+ char *extension_dir;
+
+ extension_dir = INI_STR("extension_dir");
+
+ if (strchr(*path, '/') != NULL || strchr(*path, DEFAULT_SLASH) != NULL) {
+ /* path is fine */
+ } else if (extension_dir && extension_dir[0]) {
+ char *libpath;
+ int extension_dir_len = strlen(extension_dir);
+ if (IS_SLASH(extension_dir[extension_dir_len-1])) {
+ spprintf(&libpath, 0, "%s%s", extension_dir, *path); /* SAFE */
+ } else {
+ spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, *path); /* SAFE */
+ }
+ efree(*path);
+ *path = libpath;
+ } else {
+ phpdbg_error("dl", "type=\"relpath\"", "Not a full path given or extension_dir ini setting is not set");
+
+ return NULL;
+ }
+
+ handle = DL_LOAD(*path);
+
+ if (!handle) {
+#if PHP_WIN32
+ char *err = GET_DL_ERROR();
+ if (err && *err != "") {
+ phpdbg_error("dl", "type=\"unknown\"", "%s", err);
+ LocalFree(err);
+ } else {
+ phpdbg_error("dl", "type=\"unknown\"", "Unknown reason");
+ }
+#else
+ phpdbg_error("dl", "type=\"unknown\"", "%s", GET_DL_ERROR());
+#endif
+ return NULL;
+ }
+
+#if ZEND_EXTENSIONS_SUPPORT
+ do {
+ zend_extension *new_extension;
+ zend_extension_version_info *extension_version_info;
+
+ extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "extension_version_info");
+ if (!extension_version_info) {
+ extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "_extension_version_info");
+ }
+ new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "zend_extension_entry");
+ if (!new_extension) {
+ new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "_zend_extension_entry");
+ }
+ if (!extension_version_info || !new_extension) {
+ break;
+ }
+ if (extension_version_info->zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) {
+ phpdbg_error("dl", "type=\"wrongapi\" extension=\"%s\" apineeded=\"%d\" apiinstalled=\"%d\"", "%s requires Zend Engine API version %d, which does not match the installed Zend Engine API version %d", new_extension->name, extension_version_info->zend_extension_api_no, ZEND_EXTENSION_API_NO);
+
+ goto quit;
+ } else if (strcmp(ZEND_EXTENSION_BUILD_ID, extension_version_info->build_id) && (!new_extension->build_id_check || new_extension->build_id_check(ZEND_EXTENSION_BUILD_ID) != SUCCESS)) {
+ phpdbg_error("dl", "type=\"wrongbuild\" extension=\"%s\" buildneeded=\"%s\" buildinstalled=\"%s\"", "%s was built with configuration %s, whereas running engine is %s", new_extension->name, extension_version_info->build_id, ZEND_EXTENSION_BUILD_ID);
+
+ goto quit;
+ }
+
+ *name = new_extension->name;
+
+ zend_register_extension(new_extension, handle);
+
+ if (new_extension->startup) {
+ if (new_extension->startup(new_extension) != SUCCESS) {
+ phpdbg_error("dl", "type=\"startupfailure\" extension=\"%s\"", "Unable to startup Zend extension %s", new_extension->name);
+
+ goto quit;
+ }
+ zend_append_version_info(new_extension);
+ }
+
+ return "Zend extension";
+ } while (0);
+#endif
+
+ do {
+ zend_module_entry *module_entry;
+ zend_module_entry *(*get_module)(void);
+
+ get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module");
+ if (!get_module) {
+ get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module");
+ }
+
+ if (!get_module) {
+ break;
+ }
+
+ module_entry = get_module();
+ *name = (char *) module_entry->name;
+
+ if (strcmp(ZEND_EXTENSION_BUILD_ID, module_entry->build_id)) {
+ phpdbg_error("dl", "type=\"wrongbuild\" module=\"%s\" buildneeded=\"%s\" buildinstalled=\"%s\"", "%s was built with configuration %s, whereas running engine is %s", module_entry->name, module_entry->build_id, ZEND_EXTENSION_BUILD_ID);
+
+ goto quit;
+ }
+
+ module_entry->type = MODULE_PERSISTENT;
+ module_entry->module_number = zend_next_free_module();
+ module_entry->handle = handle;
+
+ if ((module_entry = zend_register_module_ex(module_entry TSRMLS_CC)) == NULL) {
+ phpdbg_error("dl", "type=\"registerfailure\" module=\"%s\"", "Unable to register module %s", module_entry->name);
+
+ goto quit;
+ }
+
+ if (zend_startup_module_ex(module_entry TSRMLS_CC) == FAILURE) {
+ phpdbg_error("dl", "type=\"startupfailure\" module=\"%s\"", "Unable to startup module %s", module_entry->name);
+
+ goto quit;
+ }
+
+ if (module_entry->request_startup_func) {
+ if (module_entry->request_startup_func(MODULE_PERSISTENT, module_entry->module_number TSRMLS_CC) == FAILURE) {
+ phpdbg_error("dl", "type=\"initfailure\" module=\"%s\"", "Unable to initialize module %s", module_entry->name);
+
+ goto quit;
+ }
+ }
+
+ return "module";
+ } while (0);
+
+ phpdbg_error("dl", "type=\"nophpso\"", "This shared object is nor a Zend extension nor a module");
+
+quit:
+ DL_UNLOAD(handle);
+ return NULL;
+}
+
+PHPDBG_COMMAND(dl) /* {{{ */
+{
+ const char *type;
+ char *name, *path;
+
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_notice("dl", "extensiontype=\"Zend extension\"", "Zend extensions");
+ zend_llist_apply(&zend_extensions, (llist_apply_func_t) add_zendext_info TSRMLS_CC);
+ phpdbg_out("\n");
+ phpdbg_notice("dl", "extensiontype=\"module\"", "Modules");
+ zend_hash_apply(&module_registry, (apply_func_t) add_module_info TSRMLS_CC);
+ } else switch (param->type) {
+ case STR_PARAM:
+#ifdef HAVE_LIBDL
+ path = estrndup(param->str, param->len);
+
+ phpdbg_activate_err_buf(1 TSRMLS_CC);
+ if ((type = phpdbg_load_module_or_extension(&path, &name TSRMLS_CC)) == NULL) {
+ phpdbg_error("dl", "path=\"%s\" %b", "Could not load %s, not found or invalid zend extension / module: %b", path);
+ efree(name);
+ } else {
+ phpdbg_notice("dl", "extensiontype=\"%s\" name=\"%s\" path=\"%s\"", "Successfully loaded the %s %s at path %s", type, name, path);
+ }
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+ efree(path);
+#else
+ phpdbg_error("dl", "type=\"unsupported\" path=\"%.*s\"", "Cannot dynamically load %.*s - dynamic modules are not supported", (int) param->len, param->str);
+#endif
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(source) /* {{{ */
+{
+ struct stat sb;
+
+ if (VCWD_STAT(param->str, &sb) != -1) {
+ phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC);
+ } else {
+ phpdbg_error("source", "type=\"notfound\" file=\"%s\"", "Failed to stat %s, file does not exist", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(export) /* {{{ */
+{
+ FILE *handle = VCWD_FOPEN(param->str, "w+");
+
+ if (handle) {
+ phpdbg_export_breakpoints(handle TSRMLS_CC);
+ fclose(handle);
+ } else {
+ phpdbg_error("export", "type=\"openfailure\" file=\"%s\"", "Failed to open or create %s, check path and permissions", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(register) /* {{{ */
+{
+ 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("register", "function=\"%s\"", "Registered %s", lcname);
+ } else {
+ phpdbg_error("register", "type=\"notfoundc\" function=\"%s\"", "The requested function (%s) could not be found", param->str);
+ }
+ } else {
+ phpdbg_error("register", "type=\"inuse\" function=\"%s\"", "The requested name (%s) is already in use", lcname);
+ }
+
+ efree(lcname);
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(quit) /* {{{ */
+{
+ /* don't allow this to loop, ever ... */
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
+ zend_bailout();
+ }
+
+ return PHPDBG_NEXT;
+} /* }}} */
+
+PHPDBG_COMMAND(clean) /* {{{ */
+{
+ if (EG(in_execution)) {
+ phpdbg_error("inactive", "type=\"isrunning\"", "Cannot clean environment while executing");
+ return SUCCESS;
+ }
+
+ phpdbg_out("Cleaning Execution Environment\n");
+ phpdbg_xml("<cleaninfo %r>");
+
+ phpdbg_writeln("clean", "classes=\"%d\"", "Classes %d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("clean", "functions=\"%d\"", "Functions %d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("clean", "constants=\"%d\"", "Constants %d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("clean", "includes=\"%d\"", "Includes %d", zend_hash_num_elements(&EG(included_files)));
+
+ phpdbg_clean(1 TSRMLS_CC);
+
+ phpdbg_xml("</cleaninfo>");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(clear) /* {{{ */
+{
+ phpdbg_out("Clearing Breakpoints\n");
+ phpdbg_xml("<clearinfo %r>");
+
+ phpdbg_writeln("clear", "files=\"%d\"", "File %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]));
+ phpdbg_writeln("clear", "functions=\"%d\"", "Functions %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]));
+ phpdbg_writeln("clear", "methods=\"%d\"", "Methods %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]));
+ phpdbg_writeln("clear", "oplines=\"%d\"", "Oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]));
+ phpdbg_writeln("clear", "fileoplines=\"%d\"", "File oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]));
+ phpdbg_writeln("clear", "functionoplines=\"%d\"", "Function oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]));
+ phpdbg_writeln("clear", "methodoplines=\"%d\"", "Method oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]));
+ phpdbg_writeln("clear", "eval=\"%d\"", "Conditionals %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]));
+
+ phpdbg_clear_breakpoints(TSRMLS_C);
+
+ phpdbg_xml("</clearinfo>");
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_COMMAND(list) /* {{{ */
+{
+ if (!param) {
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+ } else switch (param->type) {
+ case NUMERIC_PARAM:
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+
+ case FILE_PARAM:
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+
+ case STR_PARAM:
+ phpdbg_list_function_byname(param->str, param->len TSRMLS_CC);
+ break;
+
+ case METHOD_PARAM:
+ return PHPDBG_LIST_HANDLER(method)(PHPDBG_COMMAND_ARGS);
+
+ phpdbg_default_switch_case();
+ }
+
+ 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("watch", "variable=\"%.*s\"", "Set watchpoint on %.*s", (int) param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC) /* {{{ */
+{
+ int ret = SUCCESS;
+ char *input = NULL;
+ phpdbg_param_t stack;
+
+ PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
+
+ input = phpdbg_read_input(NULL TSRMLS_CC);
+
+ if (input) {
+ do {
+ phpdbg_init_param(&stack, STACK_PARAM);
+
+ if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) {
+ phpdbg_activate_err_buf(1 TSRMLS_CC);
+
+#ifdef PHP_WIN32
+#define PARA ((phpdbg_param_t *)stack.next)->type
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE && (RUN_PARAM == PARA || EVAL_PARAM == PARA)) {
+ sigio_watcher_start();
+ }
+#endif
+ switch (ret = phpdbg_stack_execute(&stack, allow_async_unsafe TSRMLS_CC)) {
+ case FAILURE:
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (!allow_async_unsafe || phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
+ phpdbg_output_err_buf(NULL, "%b", "%b" TSRMLS_CC);
+ }
+ }
+ break;
+
+ case PHPDBG_LEAVE:
+ case PHPDBG_FINISH:
+ case PHPDBG_UNTIL:
+ case PHPDBG_NEXT: {
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+ if (!EG(in_execution) && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ phpdbg_error("command", "type=\"noexec\"", "Not running");
+ }
+ goto out;
+ }
+ }
+
+ phpdbg_activate_err_buf(0 TSRMLS_CC);
+ phpdbg_free_err_buf(TSRMLS_C);
+#ifdef PHP_WIN32
+ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE && (RUN_PARAM == PARA || EVAL_PARAM == PARA)) {
+ sigio_watcher_stop();
+ }
+#undef PARA
+#endif
+ }
+
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ PHPDBG_G(req_id) = 0;
+ } while ((input = phpdbg_read_input(NULL TSRMLS_CC)));
+ }
+
+out:
+ if (input) {
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ PHPDBG_G(req_id) = 0;
+ }
+
+ if (EG(in_execution)) {
+ phpdbg_restore_frame(TSRMLS_C);
+ }
+
+ PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE;
+
+ phpdbg_print_changed_zvals(TSRMLS_C);
+
+ return ret;
+} /* }}} */
+
+void phpdbg_clean(zend_bool full TSRMLS_DC) /* {{{ */
+{
+ /* this is implicitly required */
+ if (PHPDBG_G(ops)) {
+ destroy_op_array(PHPDBG_G(ops) TSRMLS_CC);
+ efree(PHPDBG_G(ops));
+ PHPDBG_G(ops) = NULL;
+ }
+
+ if (full) {
+ PHPDBG_G(flags) |= PHPDBG_IS_CLEANING;
+
+ zend_bailout();
+ }
+} /* }}} */
+
+static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_array, zend_bool nested TSRMLS_DC) /* {{{ */
+{
+#if PHP_VERSION_ID >= 50500
+ return zend_create_execute_data_from_op_array(op_array, nested TSRMLS_CC);
+#else
+
+#undef EX
+#define EX(element) execute_data->element
+#undef EX_CV
+#define EX_CV(var) EX(CVs)[var]
+#undef EX_CVs
+#define EX_CVs() EX(CVs)
+#undef EX_T
+#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset))
+#undef EX_Ts
+#define EX_Ts() EX(Ts)
+
+ zend_execute_data *execute_data = (zend_execute_data *)zend_vm_stack_alloc(
+ ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) +
+ ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC);
+
+ EX(CVs) = (zval ***)((char *)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)));
+ memset(EX(CVs), 0, sizeof(zval **) * op_array->last_var);
+ EX(Ts) = (temp_variable *)(((char *) EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)));
+ EX(fbc) = NULL;
+ EX(called_scope) = NULL;
+ EX(object) = NULL;
+ EX(old_error_reporting) = NULL;
+ EX(op_array) = op_array;
+ EX(symbol_table) = EG(active_symbol_table);
+ EX(prev_execute_data) = EG(current_execute_data);
+ EG(current_execute_data) = execute_data;
+ EX(nested) = nested;
+
+ if (!op_array->run_time_cache && op_array->last_cache_slot) {
+ op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*));
+ }
+
+ if (op_array->this_var != -1 && EG(This)) {
+ Z_ADDREF_P(EG(This)); /* For $this pointer */
+ if (!EG(active_symbol_table)) {
+ EX_CV(op_array->this_var) = (zval**) EX_CVs() + (op_array->last_var + op_array->this_var);
+ *EX_CV(op_array->this_var) = EG(This);
+ } else {
+ if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void **) &EX_CV(op_array->this_var))==FAILURE) {
+ Z_DELREF_P(EG(This));
+ }
+ }
+ }
+
+ EX(opline) = op_array->opcodes;
+ EG(opline_ptr) = &EX(opline);
+
+ EX(function_state).function = (zend_function *) op_array;
+ EX(function_state).arguments = NULL;
+
+ return execute_data;
+#endif
+} /* }}} */
+
+#define DO_INTERACTIVE(allow_async_unsafe) do { \
+ if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \
+ phpdbg_list_file( \
+ zend_get_executed_filename(TSRMLS_C), \
+ 3, \
+ zend_get_executed_lineno(TSRMLS_C)-1, \
+ zend_get_executed_lineno(TSRMLS_C) \
+ TSRMLS_CC \
+ ); \
+ } \
+ \
+ switch (phpdbg_interactive(allow_async_unsafe TSRMLS_CC)) { \
+ case PHPDBG_LEAVE: \
+ case PHPDBG_FINISH: \
+ case PHPDBG_UNTIL: \
+ case PHPDBG_NEXT:{ \
+ goto next; \
+ } \
+ } \
+} while (0)
+
+#if PHP_VERSION_ID >= 50500
+void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+#else
+void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+ long long flags = 0;
+ zend_ulong address = 0L;
+ zend_execute_data *execute_data;
+ zend_bool nested = 0;
+#endif
+ zend_bool original_in_execution = EG(in_execution);
+ HashTable vars;
+
+#if PHP_VERSION_ID < 50500
+ if (EG(exception)) {
+ return;
+ }
+#endif
+
+ EG(in_execution) = 1;
+
+#if PHP_VERSION_ID >= 50500
+ if (0) {
+zend_vm_enter:
+ execute_data = phpdbg_create_execute_data(EG(active_op_array), 1 TSRMLS_CC);
+ }
+ zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0);
+#else
+zend_vm_enter:
+ execute_data = phpdbg_create_execute_data(op_array, nested TSRMLS_CC);
+ nested = 1;
+ zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0);
+#endif
+
+ while (1) {
+ if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) {
+ /* resolve nth opline breakpoints */
+ phpdbg_resolve_op_array_breaks(EG(active_op_array) TSRMLS_CC);
+ }
+
+#ifdef ZEND_WIN32
+ if (EG(timed_out)) {
+ zend_timeout(0);
+ }
+#endif
+
+ /* allow conditional breakpoints and
+ initialization to access the vm uninterrupted */
+ if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) ||
+ (PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
+ /* skip possible breakpoints */
+ goto next;
+ }
+
+ /* perform seek operation */
+ if (PHPDBG_G(flags) & PHPDBG_SEEK_MASK) {
+ /* current address */
+ zend_ulong address = (zend_ulong) execute_data->opline;
+
+ /* run to next line */
+ if (PHPDBG_G(flags) & PHPDBG_IN_UNTIL) {
+ if (zend_hash_index_exists(&PHPDBG_G(seek), address)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IN_UNTIL;
+ zend_hash_clean(&PHPDBG_G(seek));
+ } else {
+ /* skip possible breakpoints */
+ goto next;
+ }
+ }
+
+ /* run to finish */
+ if (PHPDBG_G(flags) & PHPDBG_IN_FINISH) {
+ if (zend_hash_index_exists(&PHPDBG_G(seek), address)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IN_FINISH;
+ zend_hash_clean(&PHPDBG_G(seek));
+ }
+ /* skip possible breakpoints */
+ goto next;
+ }
+
+ /* break for leave */
+ if (PHPDBG_G(flags) & PHPDBG_IN_LEAVE) {
+ if (zend_hash_index_exists(&PHPDBG_G(seek), address)) {
+ PHPDBG_G(flags) &= ~PHPDBG_IN_LEAVE;
+ zend_hash_clean(&PHPDBG_G(seek));
+ phpdbg_notice("breakpoint", "id=\"leave\" file=\"%s\" line=\"%u\"", "Breaking for leave at %s:%u",
+ zend_get_executed_filename(TSRMLS_C),
+ zend_get_executed_lineno(TSRMLS_C)
+ );
+ DO_INTERACTIVE(1);
+ } else {
+ /* skip possible breakpoints */
+ goto next;
+ }
+ }
+ }
+
+ /* not while in conditionals */
+ 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(1);
+ }
+
+ /* check if some watchpoint was hit */
+ {
+ if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) {
+ DO_INTERACTIVE(1);
+ }
+ }
+
+ /* search for breakpoints */
+ {
+ phpdbg_breakbase_t *brake;
+
+ 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(1);
+ }
+ }
+
+ if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
+
+ phpdbg_out("\n");
+ phpdbg_notice("signal", "type=\"SIGINT\"", "Program received signal SIGINT");
+ DO_INTERACTIVE(1);
+ }
+
+next:
+
+ PHPDBG_G(last_line) = execute_data->opline->lineno;
+
+ /* stupid hack to make zend_do_fcall_common_helper return ZEND_VM_ENTER() instead of recursively calling zend_execute() and eventually segfaulting */
+ if ((execute_data->opline->opcode == ZEND_DO_FCALL_BY_NAME || execute_data->opline->opcode == ZEND_DO_FCALL) && execute_data->function_state.function->type == ZEND_USER_FUNCTION) {
+#if PHP_VERSION_ID < 50500
+ zend_execute = execute;
+#else
+ zend_execute_ex = execute_ex;
+#endif
+ }
+ PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
+#if PHP_VERSION_ID < 50500
+ zend_execute = phpdbg_execute_ex;
+#else
+ zend_execute_ex = phpdbg_execute_ex;
+#endif
+
+ if (PHPDBG_G(vmret) > 0) {
+ switch (PHPDBG_G(vmret)) {
+ case 1:
+ EG(in_execution) = original_in_execution;
+ zend_hash_destroy(&vars);
+ return;
+ case 2:
+#if PHP_VERSION_ID < 50500
+ op_array = EG(active_op_array);
+#endif
+ zend_hash_destroy(&vars);
+ goto zend_vm_enter;
+ break;
+ case 3:
+ execute_data = EG(current_execute_data);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen");
+} /* }}} */
+
+/* only if *not* interactive and while executing */
+void phpdbg_force_interruption(TSRMLS_D) {
+ zend_execute_data *data = EG(current_execute_data); /* should be always readable if not NULL */
+
+ PHPDBG_G(flags) |= PHPDBG_IN_SIGNAL_HANDLER;
+
+ if (data) {
+ if (data->op_array) {
+ phpdbg_notice("hardinterrupt", "opline=\"%p\" num=\"%lu\" file=\"%s\" line=\"%u\"", "Current opline: %p (op #%lu) in %s:%u", data->opline, (data->opline - data->op_array->opcodes) / sizeof(data->opline), data->op_array->filename, data->opline->lineno);
+ } else {
+ phpdbg_notice("hardinterrupt", "opline=\"%p\"", "Current opline: %p (op_array information unavailable)", data->opline);
+ }
+ } else {
+ phpdbg_notice("hardinterrupt", "", "No information available about executing context");
+ }
+
+ DO_INTERACTIVE(0);
+
+next:
+ PHPDBG_G(flags) &= ~PHPDBG_IN_SIGNAL_HANDLER;
+
+ if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
+ zend_bailout();
+ }
+}
+
+PHPDBG_COMMAND(eol) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_notice("eol", "argument required", "argument required");
+ } else switch (param->type) {
+ case STR_PARAM:
+ if (FAILURE == phpdbg_eol_global_update(param->str TSRMLS_CC)) {
+ phpdbg_notice("eol", "unknown EOL name '%s', give crlf, lf, cr", "unknown EOL name '%s', give crlf, lf, cr", param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
diff --git a/sapi/phpdbg/phpdbg_prompt.h b/sapi/phpdbg/phpdbg_prompt.h
new file mode 100644
index 0000000000..94e24df833
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_prompt.h
@@ -0,0 +1,72 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_PROMPT_H
+#define PHPDBG_PROMPT_H
+
+/* {{{ */
+void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC);
+void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC);
+int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC);
+int phpdbg_compile(TSRMLS_D);
+void phpdbg_clean(zend_bool full TSRMLS_DC);
+void phpdbg_force_interruption(TSRMLS_D);
+/* }}} */
+
+/* {{{ phpdbg command handlers */
+PHPDBG_COMMAND(exec);
+PHPDBG_COMMAND(step);
+PHPDBG_COMMAND(continue);
+PHPDBG_COMMAND(run);
+PHPDBG_COMMAND(ev);
+PHPDBG_COMMAND(until);
+PHPDBG_COMMAND(finish);
+PHPDBG_COMMAND(leave);
+PHPDBG_COMMAND(frame);
+PHPDBG_COMMAND(print);
+PHPDBG_COMMAND(break);
+PHPDBG_COMMAND(back);
+PHPDBG_COMMAND(list);
+PHPDBG_COMMAND(info);
+PHPDBG_COMMAND(clean);
+PHPDBG_COMMAND(clear);
+PHPDBG_COMMAND(help);
+PHPDBG_COMMAND(sh);
+PHPDBG_COMMAND(dl);
+PHPDBG_COMMAND(set);
+PHPDBG_COMMAND(source);
+PHPDBG_COMMAND(export);
+PHPDBG_COMMAND(register);
+PHPDBG_COMMAND(quit);
+PHPDBG_COMMAND(watch);
+PHPDBG_COMMAND(eol);
+PHPDBG_COMMAND(wait); /* }}} */
+
+/* {{{ prompt commands */
+extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */
+
+/* {{{ */
+#if PHP_VERSION_ID >= 50500
+void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
+#else
+void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC);
+#endif /* }}} */
+
+#endif /* PHPDBG_PROMPT_H */
diff --git a/sapi/phpdbg/phpdbg_rinit_hook.c b/sapi/phpdbg/phpdbg_rinit_hook.c
new file mode 100644
index 0000000000..049a782d9d
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_rinit_hook.c
@@ -0,0 +1,101 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg_rinit_hook.h"
+#include "php_ini.h"
+#include <errno.h>
+
+ZEND_DECLARE_MODULE_GLOBALS(phpdbg_webhelper);
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("phpdbg.auth", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, auth, zend_phpdbg_webhelper_globals, phpdbg_webhelper_globals)
+ STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, path, zend_phpdbg_webhelper_globals, phpdbg_webhelper_globals)
+PHP_INI_END()
+
+static inline void php_phpdbg_webhelper_globals_ctor(zend_phpdbg_webhelper_globals *pg) /* {{{ */
+{
+} /* }}} */
+
+static PHP_MINIT_FUNCTION(phpdbg_webhelper) /* {{{ */
+{
+ if (!strcmp(sapi_module.name, PHPDBG_NAME)) {
+ return SUCCESS;
+ }
+
+ ZEND_INIT_MODULE_GLOBALS(phpdbg_webhelper, php_phpdbg_webhelper_globals_ctor, NULL);
+ REGISTER_INI_ENTRIES();
+
+ return SUCCESS;
+} /* }}} */
+
+static PHP_RINIT_FUNCTION(phpdbg_webhelper) /* {{{ */
+{
+ zval *cookies = PG(http_globals)[TRACK_VARS_COOKIE];
+ zval **auth;
+
+ if (!cookies || zend_hash_find(Z_ARRVAL_P(cookies), PHPDBG_NAME "_AUTH_COOKIE", sizeof(PHPDBG_NAME "_AUTH_COOKIE"), (void **) &auth) == FAILURE || Z_STRLEN_PP(auth) != strlen(PHPDBG_WG(auth)) || strcmp(Z_STRVAL_PP(auth), PHPDBG_WG(auth))) {
+ return SUCCESS;
+ }
+
+#ifndef _WIN32
+ {
+ struct sockaddr_un sock;
+ int s = socket(AF_UNIX, SOCK_STREAM, 0);
+ int len = strlen(PHPDBG_WG(path)) + sizeof(sock.sun_family);
+ char buf[(1 << 8) + 1];
+ int buflen;
+ sock.sun_family = AF_UNIX;
+ strcpy(sock.sun_path, PHPDBG_WG(path));
+
+ if (connect(s, (struct sockaddr *)&sock, len) == -1) {
+ zend_error(E_ERROR, "Unable to connect to UNIX domain socket at %s defined by phpdbg.path ini setting. Reason: %s", PHPDBG_WG(path), strerror(errno));
+ }
+
+ char *msg = NULL;
+ char msglen[5] = {0};
+ phpdbg_webdata_compress(&msg, (int *)msglen TSRMLS_CC);
+
+ send(s, msglen, 4, 0);
+ send(s, msg, *(int *) msglen, 0);
+
+ while ((buflen = recv(s, buf, sizeof(buf) - 1, 0)) > 0) {
+ php_write(buf, buflen TSRMLS_CC);
+ }
+
+ close(s);
+
+ php_output_flush_all(TSRMLS_C);
+ zend_bailout();
+ }
+#endif
+
+ return SUCCESS;
+} /* }}} */
+
+zend_module_entry phpdbg_webhelper_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "phpdbg_webhelper",
+ NULL,
+ PHP_MINIT(phpdbg_webhelper),
+ NULL,
+ PHP_RINIT(phpdbg_webhelper),
+ NULL,
+ NULL,
+ PHPDBG_VERSION,
+ STANDARD_MODULE_PROPERTIES
+};
diff --git a/sapi/phpdbg/phpdbg_rinit_hook.h b/sapi/phpdbg/phpdbg_rinit_hook.h
new file mode 100644
index 0000000000..d28be10f8c
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_rinit_hook.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WEBHELPER_H
+#define PHPDBG_WEBHELPER_H
+
+#include "phpdbg_webdata_transfer.h"
+
+extern zend_module_entry phpdbg_webhelper_module_entry;
+#define phpext_phpdbg_webhelper_ptr &phpdbg_webhelper_module_entry
+
+#ifdef ZTS
+# define PHPDBG_WG(v) TSRMG(phpdbg_webhelper_globals_id, zend_phpdbg_webhelper_globals *, v)
+#else
+# define PHPDBG_WG(v) (phpdbg_webhelper_globals.v)
+#endif
+
+/* {{{ structs */
+ZEND_BEGIN_MODULE_GLOBALS(phpdbg_webhelper)
+ char *auth;
+ char *path;
+ZEND_END_MODULE_GLOBALS(phpdbg_webhelper) /* }}} */
+
+#endif /* PHPDBG_WEBHELPER_H */
diff --git a/sapi/phpdbg/phpdbg_set.c b/sapi/phpdbg/phpdbg_set.c
new file mode 100644
index 0000000000..fc7e788fa0
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_set.c
@@ -0,0 +1,250 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_set.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s, flags) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18], flags)
+
+const phpdbg_command_t phpdbg_set_commands[] = {
+ PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s", 0),
+#ifndef _WIN32
+ PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b", PHPDBG_ASYNC_SAFE),
+#endif
+ PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s", 0),
+ PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b", PHPDBG_ASYNC_SAFE),
+ PHPDBG_END_COMMAND
+};
+
+PHPDBG_SET(prompt) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("setprompt", "str=\"%s\"", "Current prompt: %s", phpdbg_get_prompt(TSRMLS_C));
+ } else {
+ phpdbg_set_prompt(param->str TSRMLS_CC);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(break) /* {{{ */
+{
+ switch (param->type) {
+ case NUMERIC_PARAM: {
+ 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("setbreak", "id=\"%ld\" active=\"%s\"", "Breakpoint #%ld %s", param->num, brake->disabled ? "off" : "on");
+ } else {
+ phpdbg_error("setbreak", "type=\"nobreak\" id=\"%ld\"", "Failed to find breakpoint #%ld", param->num);
+ }
+ }
+ } break;
+
+ default:
+ phpdbg_error("setbreak", "type=\"wrongargs\"", "set break used incorrectly: set break [id] <on|off>");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(breaks) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("setbreaks", "active=\"%s\"", "Breakpoints %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;
+
+ default:
+ phpdbg_error("setbreaks", "type=\"wrongargs\"", "set breaks used incorrectly: set breaks <on|off>");
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+#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("setcolor", "type=\"nocolor\"", "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("setcolor", "type=\"prompt\" color=\"%s\" code=\"%s\"", "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("setcolor", "type=\"error\" color=\"%s\" code=\"%s\"", "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("setcolor", "type=\"notice\" color=\"%s\" code=\"%s\"", "setting notice color to %s (%s)", color->name, color->code);
+ phpdbg_set_color(PHPDBG_COLOR_NOTICE, color TSRMLS_CC);
+ break;
+
+ default:
+ phpdbg_error("setcolor", "type=\"invalidtype\"", "Failed to find the requested element (%s)", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(colors) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("setcolors", "active=\"%s\"", "Colors %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;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_COLOURED;
+ }
+ } break;
+
+ default:
+ phpdbg_error("setcolors", "type=\"wrongargs\"", "set colors used incorrectly: set colors <on|off>");
+ }
+
+ return SUCCESS;
+} /* }}} */
+#endif
+
+PHPDBG_SET(oplog) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_notice("setoplog", "active=\"%s\"", "Oplog %s", PHPDBG_G(oplog) ? "on" : "off");
+ } else switch (param->type) {
+ case STR_PARAM: {
+ /* open oplog */
+ FILE *old = PHPDBG_G(oplog);
+
+ PHPDBG_G(oplog) = fopen(param->str, "w+");
+ if (!PHPDBG_G(oplog)) {
+ phpdbg_error("setoplog", "type=\"openfailure\" file=\"%s\"", "Failed to open %s for oplog", param->str);
+ PHPDBG_G(oplog) = old;
+ } else {
+ if (old) {
+ phpdbg_notice("setoplog", "type=\"closingold\"", "Closing previously open oplog");
+ fclose(old);
+ }
+
+ phpdbg_notice("setoplog", "file=\"%s\"", "Successfully opened oplog %s", param->str);
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(quiet) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("setquiet", "active=\"%s\"", "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("setstepping", "type=\"%s\"", "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("setstepping", "type=\"wrongargs\"", "usage set stepping [<opcode|line>]");
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(refcount) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("setrefcount", "active=\"%s\"", "Showing refcounts %s", PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ PHPDBG_G(flags) |= PHPDBG_SHOW_REFCOUNTS;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_SHOW_REFCOUNTS;
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_set.h b/sapi/phpdbg/phpdbg_set.h
new file mode 100644
index 0000000000..dea61ed382
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_set.h
@@ -0,0 +1,42 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_SET_H
+#define PHPDBG_SET_H
+
+#include "phpdbg_cmd.h"
+
+#define PHPDBG_SET(name) PHPDBG_COMMAND(set_##name)
+
+PHPDBG_SET(prompt);
+#ifndef _WIN32
+PHPDBG_SET(color);
+PHPDBG_SET(colors);
+#endif
+PHPDBG_SET(oplog);
+PHPDBG_SET(break);
+PHPDBG_SET(breaks);
+PHPDBG_SET(quiet);
+PHPDBG_SET(stepping);
+PHPDBG_SET(refcount);
+
+extern const phpdbg_command_t phpdbg_set_commands[];
+
+#endif /* PHPDBG_SET_H */
diff --git a/sapi/phpdbg/phpdbg_sigio_win32.c b/sapi/phpdbg/phpdbg_sigio_win32.c
new file mode 100644
index 0000000000..24e9ac0e0a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_sigio_win32.c
@@ -0,0 +1,120 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 7-4 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+
+#include <signal.h>
+
+#include "phpdbg.h"
+#include "phpdbg_sigio_win32.h"
+
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+
+VOID
+SigIoWatcherThread(VOID *p)
+{
+ zend_uchar sig;
+ struct win32_sigio_watcher_data *swd = (struct win32_sigio_watcher_data *)p;
+#ifdef ZTS
+ void ***tsrm_ls = swd->tsrm_ls;
+top:
+ (void)phpdbg_consume_bytes(swd->fd, &sig, 1, -1, tsrm_ls);
+#else
+top:
+ (void)phpdbg_consume_bytes(swd->fd, &sig, 1, -1);
+#endif
+
+
+ if (3 == sig) {
+ printf("signaled, got %d", sig);
+ /* XXX completely not sure it is done right here */
+ if (swd->flags & PHPDBG_IS_INTERACTIVE) {
+ if (raise(sig)) {
+ /* just out*/
+ exit(0);
+ }
+ }
+ if (swd->flags & PHPDBG_IS_SIGNALED) {
+ phpdbg_set_sigsafe_mem(&sig TSRMLS_CC);
+ zend_try {
+ phpdbg_force_interruption(TSRMLS_C);
+ } zend_end_try();
+ phpdbg_clear_sigsafe_mem(TSRMLS_C);
+ }
+ /* XXX set signaled flag to the caller thread, question is - whether it's needed */
+ ExitThread(sig);
+ } else {
+ goto top;
+ }
+}
+
+
+/* Start this only for the time of the run or eval command,
+for so long that the main thread is busy serving some debug
+session. */
+void
+sigio_watcher_start(void)
+{
+ TSRMLS_FETCH();
+
+ PHPDBG_G(swd).fd = PHPDBG_G(io)[PHPDBG_STDIN].fd;
+ PHPDBG_G(swd).running = 1;
+ PHPDBG_G(swd).flags = PHPDBG_G(flags);
+#ifdef ZTS
+ PHPDBG_G(swd).tsrm_ls = tsrm_ls;
+#endif
+
+ PHPDBG_G(sigio_watcher_thread) = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)SigIoWatcherThread,
+ &PHPDBG_G(swd),
+ 0,
+ NULL);
+}
+
+void
+sigio_watcher_stop(void)
+{
+ DWORD waited;
+ TSRMLS_FETCH();
+
+ if (INVALID_HANDLE_VALUE == PHPDBG_G(sigio_watcher_thread)) {
+ /* it probably did bail out already */
+ return;
+ }
+
+ waited = WaitForSingleObject(PHPDBG_G(sigio_watcher_thread), 300);
+
+ if (WAIT_OBJECT_0 != waited) {
+ if (!CancelSynchronousIo(PHPDBG_G(sigio_watcher_thread))) {
+ /* error out */
+ }
+
+ if (!TerminateThread(PHPDBG_G(sigio_watcher_thread), 0)) {
+ /* error out */
+ }
+ }
+
+ PHPDBG_G(swd).fd = -1;
+ PHPDBG_G(swd).running = 0;
+ PHPDBG_G(swd).flags = 0;
+ PHPDBG_G(sigio_watcher_thread) = INVALID_HANDLE_VALUE;
+}
+
diff --git a/sapi/phpdbg/phpdbg_sigio_win32.h b/sapi/phpdbg/phpdbg_sigio_win32.h
new file mode 100644
index 0000000000..796b477f93
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_sigio_win32.h
@@ -0,0 +1,42 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 7-4 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+
+#ifndef PHPDBG_SIGIO_WIN32_H
+#define PHPDBG_SIGIO_WIN32_H
+
+#include "phpdbg.h"
+#include "phpdbg_prompt.h"
+#include "phpdbg_io.h"
+
+struct win32_sigio_watcher_data {
+ zend_ulong flags;
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+ int fd;
+ zend_uchar running;
+};
+
+void
+sigio_watcher_start(void);
+
+void
+sigio_watcher_stop(void);
+
+#endif /* PHPDBG_SIGIO_WIN32_H */
diff --git a/sapi/phpdbg/phpdbg_sigsafe.c b/sapi/phpdbg/phpdbg_sigsafe.c
new file mode 100644
index 0000000000..7238ad102f
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_sigsafe.c
@@ -0,0 +1,111 @@
+#include "phpdbg_sigsafe.h"
+#include "phpdbg.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+static inline void zend_mm_init(zend_mm_heap *heap) {
+ zend_mm_free_block *p;
+ int i;
+
+ heap->free_bitmap = 0;
+ heap->large_free_bitmap = 0;
+#if ZEND_MM_CACHE
+ heap->cached = 0;
+ memset(heap->cache, 0, sizeof(heap->cache));
+#endif
+#if ZEND_MM_CACHE_STAT
+ for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
+ heap->cache_stat[i].count = 0;
+ }
+#endif
+ p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0);
+ for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
+ p->next_free_block = p;
+ p->prev_free_block = p;
+ p = (zend_mm_free_block *)((char *)p + sizeof(zend_mm_free_block *) * 2);
+ heap->large_free_buckets[i] = NULL;
+ }
+ heap->rest_buckets[0] = heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(heap);
+ heap->rest_count = 0;
+}
+
+static zend_mm_storage* zend_mm_mem_init(void *params) {
+ TSRMLS_FETCH();
+
+ return &PHPDBG_G(sigsafe_mem).storage;
+}
+
+static void zend_mm_mem_dummy(zend_mm_storage *storage) {
+}
+
+#define STR(x) #x
+#define EXP_STR(x) STR(x)
+
+static zend_mm_segment* zend_mm_mem_alloc(zend_mm_storage *storage, size_t size) {
+ TSRMLS_FETCH();
+
+ if (EXPECTED(size == PHPDBG_SIGSAFE_MEM_SIZE && !PHPDBG_G(sigsafe_mem).allocated)) {
+ PHPDBG_G(sigsafe_mem).allocated = 1;
+ return (zend_mm_segment *) PHPDBG_G(sigsafe_mem).mem;
+ }
+
+ write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Tried to allocate more than " EXP_STR(PHPDBG_SIGSAFE_MEM_SIZE) " bytes from stack memory in signal handler ... bailing out of signal handler\n"));
+
+ if (*EG(bailout)) {
+ LONGJMP(*EG(bailout), FAILURE);
+ }
+
+ write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Bailed out without a bailout address in signal handler!\n"));
+
+ return NULL;
+}
+
+static zend_mm_segment* zend_mm_mem_realloc(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size) {
+ return zend_mm_mem_alloc(storage, size);
+}
+
+static void zend_mm_mem_free(zend_mm_storage *storage, zend_mm_segment *ptr) {
+}
+
+static const zend_mm_mem_handlers phpdbg_handlers_sigsafe_mem = { "stack", zend_mm_mem_init, zend_mm_mem_dummy, zend_mm_mem_dummy, zend_mm_mem_alloc, zend_mm_mem_realloc, zend_mm_mem_free };
+
+void phpdbg_set_sigsafe_mem(char *buffer TSRMLS_DC) {
+ phpdbg_signal_safe_mem *mem = &PHPDBG_G(sigsafe_mem);
+ mem->old_heap = zend_mm_set_heap(&mem->heap TSRMLS_CC);
+ mem->mem = buffer;
+ mem->allocated = 0;
+
+ mem->storage.handlers = &phpdbg_handlers_sigsafe_mem;
+ mem->heap.storage = &mem->storage;
+ mem->heap.block_size = PHPDBG_SIGSAFE_MEM_SIZE;
+ mem->heap.compact_size = 0;
+ mem->heap.segments_list = NULL;
+ zend_mm_init(&mem->heap);
+#if ZEND_MM_CACHE_STAT
+ memset(mem->heap.cache_stat, 0, sizeof(mem->heap.cache_stat));
+#endif
+ mem->heap.use_zend_alloc = 1;
+ mem->heap.real_size = 0;
+ mem->heap.overflow = 0;
+ mem->heap.real_peak = 0;
+ mem->heap.limit = PHPDBG_SIGSAFE_MEM_SIZE;
+ mem->heap.size = 0;
+ mem->heap.peak = 0;
+ mem->heap.internal = 0;
+ mem->heap.reserve = NULL;
+ mem->heap.reserve_size = 0;
+}
+
+zend_mm_heap *phpdbg_original_heap_sigsafe_mem(TSRMLS_D) {
+ return PHPDBG_G(sigsafe_mem).old_heap;
+}
+
+void phpdbg_clear_sigsafe_mem(TSRMLS_D) {
+ zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem(TSRMLS_C) TSRMLS_CC);
+ PHPDBG_G(sigsafe_mem).mem = NULL;
+}
+
+zend_bool phpdbg_active_sigsafe_mem(TSRMLS_D) {
+ return !!PHPDBG_G(sigsafe_mem).mem;
+}
+
diff --git a/sapi/phpdbg/phpdbg_sigsafe.h b/sapi/phpdbg/phpdbg_sigsafe.h
new file mode 100644
index 0000000000..6ed74f53db
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_sigsafe.h
@@ -0,0 +1,25 @@
+#ifndef PHPDBG_SIGSAFE_H
+#define PHPDBG_SIGSAFE_H
+
+#include "zend_mm_structs.h"
+
+#define PHPDBG_SIGSAFE_MEM_SIZE (1 << 20)
+
+typedef struct {
+ char *mem;
+ zend_bool allocated;
+ zend_mm_heap heap;
+ zend_mm_heap *old_heap;
+ zend_mm_storage storage;
+} phpdbg_signal_safe_mem;
+
+#include "phpdbg.h"
+
+zend_bool phpdbg_active_sigsafe_mem(TSRMLS_D);
+
+void phpdbg_set_sigsafe_mem(char *mem TSRMLS_DC);
+void phpdbg_clear_sigsafe_mem(TSRMLS_D);
+
+zend_mm_heap *phpdbg_original_heap_sigsafe_mem(TSRMLS_D);
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
new file mode 100644
index 0000000000..85b6eeb3b5
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -0,0 +1,642 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+
+#include "php.h"
+#include "phpdbg.h"
+#include "phpdbg_opcode.h"
+#include "phpdbg_utils.h"
+
+#if defined(HAVE_SYS_IOCTL_H)
+# include "sys/ioctl.h"
+# ifndef GWINSZ_IN_SYS_IOCTL
+# include <termios.h>
+# endif
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+/* {{{ color structures */
+const static phpdbg_color_t colors[] = {
+ PHPDBG_COLOR_D("none", "0;0"),
+
+ PHPDBG_COLOR_D("white", "0;64"),
+ PHPDBG_COLOR_D("white-bold", "1;64"),
+ PHPDBG_COLOR_D("white-underline", "4;64"),
+ PHPDBG_COLOR_D("red", "0;31"),
+ PHPDBG_COLOR_D("red-bold", "1;31"),
+ PHPDBG_COLOR_D("red-underline", "4;31"),
+ PHPDBG_COLOR_D("green", "0;32"),
+ PHPDBG_COLOR_D("green-bold", "1;32"),
+ PHPDBG_COLOR_D("green-underline", "4;32"),
+ PHPDBG_COLOR_D("yellow", "0;33"),
+ PHPDBG_COLOR_D("yellow-bold", "1;33"),
+ PHPDBG_COLOR_D("yellow-underline", "4;33"),
+ PHPDBG_COLOR_D("blue", "0;34"),
+ PHPDBG_COLOR_D("blue-bold", "1;34"),
+ PHPDBG_COLOR_D("blue-underline", "4;34"),
+ PHPDBG_COLOR_D("purple", "0;35"),
+ PHPDBG_COLOR_D("purple-bold", "1;35"),
+ PHPDBG_COLOR_D("purple-underline", "4;35"),
+ PHPDBG_COLOR_D("cyan", "0;36"),
+ PHPDBG_COLOR_D("cyan-bold", "1;36"),
+ PHPDBG_COLOR_D("cyan-underline", "4;36"),
+ PHPDBG_COLOR_D("black", "0;30"),
+ PHPDBG_COLOR_D("black-bold", "1;30"),
+ PHPDBG_COLOR_D("black-underline", "4;30"),
+ 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)
+ return 0;
+
+ for (; *str; str++) {
+ if (isspace(*str) || *str == '-') {
+ continue;
+ }
+ return isdigit(*str);
+ }
+ return 0;
+} /* }}} */
+
+PHPDBG_API int phpdbg_is_empty(const char *str) /* {{{ */
+{
+ if (!str)
+ return 1;
+
+ for (; *str; str++) {
+ if (isspace(*str)) {
+ continue;
+ }
+ return 0;
+ }
+ return 1;
+} /* }}} */
+
+PHPDBG_API int phpdbg_is_addr(const char *str) /* {{{ */
+{
+ return str[0] && str[1] && memcmp(str, "0x", 2) == 0;
+} /* }}} */
+
+PHPDBG_API int phpdbg_is_class_method(const char *str, size_t len, char **class, char **method) /* {{{ */
+{
+ char *sep = NULL;
+
+ if (strstr(str, "#") != NULL)
+ return 0;
+
+ if (strstr(str, " ") != NULL)
+ return 0;
+
+ sep = strstr(str, "::");
+
+ if (!sep || sep == str || sep+2 == str+len-1) {
+ return 0;
+ }
+
+ if (class != NULL) {
+
+ if (str[0] == '\\') {
+ str++;
+ len--;
+ }
+
+ *class = estrndup(str, sep - str);
+ (*class)[sep - str] = 0;
+ }
+
+ if (method != NULL) {
+ *method = estrndup(sep+2, str + len - (sep + 2));
+ }
+
+ return 1;
+} /* }}} */
+
+PHPDBG_API char *phpdbg_resolve_path(const char *path TSRMLS_DC) /* {{{ */
+{
+ char resolved_name[MAXPATHLEN];
+
+ if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
+ return NULL;
+ }
+
+ return estrdup(resolved_name);
+} /* }}} */
+
+PHPDBG_API const char *phpdbg_current_file(TSRMLS_D) /* {{{ */
+{
+ const char *file = zend_get_executed_filename(TSRMLS_C);
+
+ if (memcmp(file, "[no active file]", sizeof("[no active file]")) == 0) {
+ return PHPDBG_G(exec);
+ }
+
+ return file;
+} /* }}} */
+
+PHPDBG_API const zend_function *phpdbg_get_function(const char *fname, const char *cname TSRMLS_DC) /* {{{ */
+{
+ zend_function *func = NULL;
+ size_t fname_len = strlen(fname);
+ char *lcname = zend_str_tolower_dup(fname, fname_len);
+
+ if (cname) {
+ zend_class_entry **ce;
+ size_t cname_len = strlen(cname);
+ char *lc_cname = zend_str_tolower_dup(cname, cname_len);
+ int ret = zend_lookup_class(lc_cname, cname_len, &ce TSRMLS_CC);
+
+ efree(lc_cname);
+
+ if (ret == SUCCESS) {
+ zend_hash_find(&(*ce)->function_table, lcname, fname_len+1,
+ (void**)&func);
+ }
+ } else {
+ zend_hash_find(EG(function_table), lcname, fname_len+1,
+ (void**)&func);
+ }
+
+ efree(lcname);
+ return func;
+} /* }}} */
+
+PHPDBG_API char *phpdbg_trim(const char *str, size_t len, size_t *new_len) /* {{{ */
+{
+ const char *p = str;
+ char *new = NULL;
+
+ while (p && isspace(*p)) {
+ ++p;
+ --len;
+ }
+
+ while (*p && isspace(*(p + len -1))) {
+ --len;
+ }
+
+ if (len == 0) {
+ new = estrndup("", sizeof(""));
+ *new_len = 0;
+ } else {
+ new = estrndup(p, len);
+ *(new + len) = '\0';
+
+ if (new_len) {
+ *new_len = len;
+ }
+ }
+
+ return new;
+
+} /* }}} */
+
+PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC) /* {{{ */
+{
+ const phpdbg_color_t *color = colors;
+
+ while (color && color->name) {
+ if (name_length == color->name_length &&
+ memcmp(name, color->name, name_length) == SUCCESS) {
+ phpdbg_debug("phpdbg_get_color(%s, %lu): %s", name, name_length, color->code);
+ return color;
+ }
+ ++color;
+ }
+
+ phpdbg_debug("phpdbg_get_color(%s, %lu): failed", name, name_length);
+
+ return NULL;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC) /* {{{ */
+{
+ PHPDBG_G(colors)[element] = color;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC) /* {{{ */
+{
+ const phpdbg_color_t *color = phpdbg_get_color(name, name_length TSRMLS_CC);
+
+ if (color) {
+ phpdbg_set_color(element, color TSRMLS_CC);
+ } else PHPDBG_G(colors)[element] = colors;
+} /* }}} */
+
+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 */
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ PHPDBG_G(prompt)[1] = NULL;
+ }
+ /* free old prompt */
+ if (PHPDBG_G(prompt)[0]) {
+ free(PHPDBG_G(prompt)[0]);
+ PHPDBG_G(prompt)[0] = NULL;
+ }
+
+ /* copy new prompt */
+ PHPDBG_G(prompt)[0] = strdup(prompt);
+} /* }}} */
+
+PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
+{
+ /* find cached prompt */
+ if (PHPDBG_G(prompt)[1]) {
+ return PHPDBG_G(prompt)[1];
+ }
+
+ /* create cached prompt */
+#ifndef HAVE_LIBEDIT
+ /* TODO: libedit doesn't seems to support coloured prompt */
+ if ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED)) {
+ asprintf(
+ &PHPDBG_G(prompt)[1], "\033[%sm%s\033[0m ",
+ PHPDBG_G(colors)[PHPDBG_COLOR_PROMPT]->code,
+ PHPDBG_G(prompt)[0]);
+ } else
+#endif
+ {
+ asprintf(
+ &PHPDBG_G(prompt)[1], "%s ",
+ PHPDBG_G(prompt)[0]);
+ }
+
+ return PHPDBG_G(prompt)[1];
+} /* }}} */
+
+int phpdbg_rebuild_symtable(TSRMLS_D) {
+ if (!EG(active_op_array)) {
+ phpdbg_error("inactive", "type=\"op_array\"", "No active op array!");
+ return FAILURE;
+ }
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+
+ if (!EG(active_symbol_table)) {
+ phpdbg_error("inactive", "type=\"symbol_table\"", "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) && defined(TIOCGWINSZ)
+ struct winsize w;
+
+ columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 80;
+#else
+ columns = 80;
+#endif
+ return columns;
+} /* }}} */
+
+PHPDBG_API void phpdbg_set_async_io(int fd) {
+#ifndef _WIN32
+ int flags;
+ fcntl(STDIN_FILENO, F_SETOWN, getpid());
+ flags = fcntl(STDIN_FILENO, F_GETFL);
+ fcntl(STDIN_FILENO, F_SETFL, flags | FASYNC);
+#endif
+}
+
+int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC) {
+ if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
+ char *lc_name, *lc_free;
+ int lc_length, ret = FAILURE;
+
+ if (name == NULL || !name_length) {
+ return FAILURE;
+ }
+
+ lc_free = lc_name = emalloc(name_length + 1);
+ zend_str_tolower_copy(lc_name, name, name_length);
+ lc_length = name_length + 1;
+
+ if (lc_name[0] == '\\') {
+ lc_name += 1;
+ lc_length -= 1;
+ }
+
+ phpdbg_try_access {
+ ret = zend_hash_find(EG(class_table), lc_name, lc_length, (void **) &ce);
+ } phpdbg_catch_access {
+ phpdbg_error("signalsegv", "class=\"%.*s\"", "Could not fetch class %.*s, invalid data source", name_length, name);
+ } phpdbg_end_try_access();
+
+ efree(lc_free);
+ return ret;
+ } else {
+ return zend_lookup_class(name, name_length, ce TSRMLS_CC);
+ }
+}
+
+char *phpdbg_get_property_key(char *key) {
+ if (*key != 0) {
+ return key;
+ }
+ return strchr(key + 1, 0) + 1;
+}
+
+static int phpdbg_parse_variable_arg_wrapper(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv, phpdbg_parse_var_func callback TSRMLS_DC) {
+ return callback(name, len, keyname, keylen, parent, zv TSRMLS_CC);
+}
+
+PHPDBG_API int phpdbg_parse_variable(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_func callback, zend_bool silent TSRMLS_DC) {
+ return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_parse_variable_arg_wrapper, silent, callback TSRMLS_CC);
+}
+
+PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_with_arg_func callback, zend_bool silent, void *arg TSRMLS_DC) {
+ int ret = FAILURE;
+ zend_bool new_index = 1;
+ char *last_index;
+ size_t 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));
+ size_t namelen;
+ char *name;
+ char *keyname = estrndup(last_index, index_len);
+ zend_hash_get_current_key_zval_ex(parent, key, &position);
+ convert_to_string(key);
+ name = emalloc(i + Z_STRLEN_P(key) + 2);
+ namelen = sprintf(name, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":"");
+ efree(key);
+
+ ret = callback(name, namelen, keyname, index_len, parent, zv, arg TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
+ } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
+ phpdbg_parse_variable_with_arg(input, len, Z_OBJPROP_PP(zv), i, callback, silent, arg TSRMLS_CC);
+ } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
+ phpdbg_parse_variable_with_arg(input, len, Z_ARRVAL_PP(zv), i, callback, silent, arg 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("variable", "type=\"undefined\" variable=\"%.*s\"", "%.*s is undefined", (int) i, input);
+ }
+ return FAILURE;
+ }
+ last_index[index_len] = last_chr;
+ if (i == len) {
+ char *name = estrndup(input, len);
+ char *keyname = estrndup(last_index, index_len);
+
+ ret = callback(name, len, keyname, index_len, parent, zv, arg 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("variable", "type=\"notiterable\" variable=\"%.*s\"", "%.*s is nor an array nor an object", (int) i, input);
+ return FAILURE;
+ }
+ index_len = 0;
+ }
+ }
+
+ return ret;
+ error:
+ phpdbg_error("variable", "type=\"invalidinput\"", "Malformed input");
+ return FAILURE;
+}
+
+static int phpdbg_xml_array_element_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) {
+ phpdbg_xml("<element");
+
+ phpdbg_try_access {
+ if (hash_key->nKeyLength == 0) { /* numeric key */
+ phpdbg_xml(" name=\"%ld\"", hash_key->h);
+ } else { /* string key */
+ phpdbg_xml(" name=\"%.*s\"", hash_key->nKeyLength - 1, hash_key->arKey);
+ }
+ } phpdbg_catch_access {
+ phpdbg_xml(" severity=\"error\" ></element>");
+ return 0;
+ } phpdbg_end_try_access();
+
+ phpdbg_xml(">");
+
+ phpdbg_xml_var_dump(zv TSRMLS_CC);
+
+ phpdbg_xml("</element>");
+
+ return 0;
+}
+
+static int phpdbg_xml_object_property_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) {
+ phpdbg_xml("<property");
+
+ phpdbg_try_access {
+ if (hash_key->nKeyLength == 0) { /* numeric key */
+ phpdbg_xml(" name=\"%ld\"", hash_key->h);
+ } else { /* string key */
+ const char *prop_name, *class_name;
+ int unmangle = zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name);
+
+ if (class_name && unmangle == SUCCESS) {
+ phpdbg_xml(" name=\"%s\"", prop_name);
+ if (class_name[0] == '*') {
+ phpdbg_xml(" protection=\"protected\"");
+ } else {
+ phpdbg_xml(" class=\"%s\" protection=\"private\"", class_name);
+ }
+ } else {
+ phpdbg_xml(" name=\"%.*s\" protection=\"public\"", hash_key->nKeyLength - 1, hash_key->arKey);
+ }
+ }
+ } phpdbg_catch_access {
+ phpdbg_xml(" severity=\"error\" ></property>");
+ return 0;
+ } phpdbg_end_try_access();
+
+ phpdbg_xml(">");
+
+ phpdbg_xml_var_dump(zv TSRMLS_CC);
+
+ phpdbg_xml("</property>");
+
+ return 0;
+}
+
+#define COMMON (Z_ISREF_PP(zv) ? "&" : "")
+
+PHPDBG_API void phpdbg_xml_var_dump(zval **zv TSRMLS_DC) {
+ HashTable *myht;
+ const char *class_name;
+ zend_uint class_name_len;
+ int (*element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*);
+ int is_temp;
+
+ phpdbg_try_access {
+ switch (Z_TYPE_PP(zv)) {
+ case IS_BOOL:
+ phpdbg_xml("<bool refstatus=\"%s\" value=\"%s\" />", COMMON, Z_LVAL_PP(zv) ? "true" : "false");
+ break;
+ case IS_NULL:
+ phpdbg_xml("<null refstatus=\"%s\" />", COMMON);
+ break;
+ case IS_LONG:
+ phpdbg_xml("<int refstatus=\"%s\" value=\"%ld\" />", COMMON, Z_LVAL_PP(zv));
+ break;
+ case IS_DOUBLE:
+ phpdbg_xml("<float refstatus=\"%s\" value=\"%.*G\" />", COMMON, (int) EG(precision), Z_DVAL_PP(zv));
+ break;
+ case IS_STRING:
+ phpdbg_xml("<string refstatus=\"%s\" length=\"%d\" value=\"%.*s\" />", COMMON, Z_STRLEN_PP(zv), Z_STRLEN_PP(zv), Z_STRVAL_PP(zv));
+ break;
+ case IS_ARRAY:
+ myht = Z_ARRVAL_PP(zv);
+ if (++myht->nApplyCount > 1) {
+ phpdbg_xml("<recursion />");
+ --myht->nApplyCount;
+ break;
+ }
+ phpdbg_xml("<array refstatus=\"%s\" num=\"%d\">", COMMON, zend_hash_num_elements(myht));
+ element_dump_func = phpdbg_xml_array_element_dump;
+ is_temp = 0;
+ goto head_done;
+ case IS_OBJECT:
+ myht = Z_OBJDEBUG_PP(zv, is_temp);
+ if (myht && ++myht->nApplyCount > 1) {
+ phpdbg_xml("<recursion />");
+ --myht->nApplyCount;
+ break;
+ }
+
+ if (Z_OBJ_HANDLER(**zv, get_class_name)) {
+ Z_OBJ_HANDLER(**zv, get_class_name)(*zv, &class_name, &class_name_len, 0 TSRMLS_CC);
+ phpdbg_xml("<object refstatus=\"%s\" class=\"%s\" id=\"%d\" num=\"%d\">", COMMON, class_name, Z_OBJ_HANDLE_PP(zv), myht ? zend_hash_num_elements(myht) : 0);
+ efree((char*)class_name);
+ } else {
+ phpdbg_xml("<object refstatus=\"%s\" class=\"\" id=\"%d\" num=\"%d\">", COMMON, Z_OBJ_HANDLE_PP(zv), myht ? zend_hash_num_elements(myht) : 0);
+ }
+ element_dump_func = phpdbg_xml_object_property_dump;
+head_done:
+ if (myht) {
+ zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) element_dump_func, 0);
+ --myht->nApplyCount;
+ if (is_temp) {
+ zend_hash_destroy(myht);
+ efree(myht);
+ }
+ }
+ if (Z_TYPE_PP(zv) == IS_ARRAY) {
+ phpdbg_xml("</array>");
+ } else {
+ phpdbg_xml("</object>");
+ }
+ break;
+ case IS_RESOURCE: {
+ const char *type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(zv) TSRMLS_CC);
+ phpdbg_xml("<resource refstatus=\"%s\" id=\"%ld\" type=\"%ld\" />", COMMON, Z_LVAL_PP(zv), type_name ? type_name : "unknown");
+ break;
+ }
+ default:
+ break;
+ }
+ } phpdbg_end_try_access();
+}
diff --git a/sapi/phpdbg/phpdbg_utils.h b/sapi/phpdbg/phpdbg_utils.h
new file mode 100644
index 0000000000..9038aa7457
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_utils.h
@@ -0,0 +1,140 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_UTILS_H
+#define PHPDBG_UTILS_H
+
+/**
+ * Input scan functions
+ */
+PHPDBG_API int phpdbg_is_numeric(const char*);
+PHPDBG_API int phpdbg_is_empty(const char*);
+PHPDBG_API int phpdbg_is_addr(const char*);
+PHPDBG_API int phpdbg_is_class_method(const char*, size_t, char**, char**);
+PHPDBG_API const char *phpdbg_current_file(TSRMLS_D);
+PHPDBG_API char *phpdbg_resolve_path(const char* TSRMLS_DC);
+PHPDBG_API char *phpdbg_trim(const char*, size_t, size_t*);
+PHPDBG_API const zend_function *phpdbg_get_function(const char*, const char* TSRMLS_DC);
+
+/* {{{ Color Management */
+#define PHPDBG_COLOR_LEN 12
+#define PHPDBG_COLOR_D(color, code) \
+ {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
+
+typedef struct _phpdbg_color_t {
+ char *name;
+ size_t name_length;
+ 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 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); /* }}} */
+
+PHPDBG_API void phpdbg_set_async_io(int fd);
+
+int phpdbg_rebuild_symtable(TSRMLS_D);
+
+#if PHP_VERSION_ID < 50500
+/* copy from zend_hash.c PHP 5.5 for 5.4 compatibility */
+static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) {
+ Bucket *p;
+
+ p = pos ? (*pos) : ht->pInternalPointer;
+
+ if (!p) {
+ Z_TYPE_P(key) = IS_NULL;
+ } else if (p->nKeyLength) {
+ Z_TYPE_P(key) = IS_STRING;
+ Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char*)p->arKey : estrndup(p->arKey, p->nKeyLength - 1);
+ Z_STRLEN_P(key) = p->nKeyLength - 1;
+ } else {
+ Z_TYPE_P(key) = IS_LONG;
+ Z_LVAL_P(key) = p->h;
+ }
+}
+#endif
+
+int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC);
+
+char *phpdbg_get_property_key(char *key);
+
+typedef int (*phpdbg_parse_var_func)(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv TSRMLS_DC);
+typedef int (*phpdbg_parse_var_with_arg_func)(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv, void *arg TSRMLS_DC);
+
+PHPDBG_API int phpdbg_parse_variable(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_func callback, zend_bool silent TSRMLS_DC);
+PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_with_arg_func callback, zend_bool silent, void *arg TSRMLS_DC);
+
+PHPDBG_API void phpdbg_xml_var_dump(zval **zv TSRMLS_DC);
+
+#ifdef ZTS
+#define PHPDBG_OUTPUT_BACKUP_DEFINES() \
+ zend_output_globals *output_globals_ptr; \
+ zend_output_globals original_output_globals; \
+ output_globals_ptr = (zend_output_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)];
+#else
+#define PHPDBG_OUTPUT_BACKUP_DEFINES() \
+ zend_output_globals *output_globals_ptr; \
+ zend_output_globals original_output_globals; \
+ output_globals_ptr = &output_globals;
+#endif
+
+#define PHPDBG_OUTPUT_BACKUP_SWAP() \
+ original_output_globals = *output_globals_ptr; \
+ memset(output_globals_ptr, 0, sizeof(zend_output_globals)); \
+ php_output_activate(TSRMLS_C);
+
+#define PHPDBG_OUTPUT_BACKUP() \
+ PHPDBG_OUTPUT_BACKUP_DEFINES() \
+ PHPDBG_OUTPUT_BACKUP_SWAP()
+
+#define PHPDBG_OUTPUT_BACKUP_RESTORE() \
+ php_output_deactivate(TSRMLS_C); \
+ *output_globals_ptr = original_output_globals;
+
+#endif /* PHPDBG_UTILS_H */
diff --git a/sapi/phpdbg/phpdbg_wait.c b/sapi/phpdbg/phpdbg_wait.c
new file mode 100644
index 0000000000..ea506a2d93
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_wait.c
@@ -0,0 +1,419 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg_wait.h"
+#include "phpdbg_prompt.h"
+#include "ext/json/JSON_parser.h"
+#include "ext/standard/basic_functions.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+ZEND_EXTERN_MODULE_GLOBALS(json);
+
+static void phpdbg_rebuild_http_globals_array(int type, const char *name TSRMLS_DC) {
+ zval **zvpp;
+ if (PG(http_globals)[type]) {
+ zval_dtor(PG(http_globals)[type]);
+ }
+ if (zend_hash_find(&EG(symbol_table), name, strlen(name) + 1, (void **) &zvpp) == SUCCESS) {
+ Z_SET_REFCOUNT_PP(zvpp, 2);
+ PG(http_globals)[type] = *zvpp;
+ }
+}
+
+
+static int phpdbg_dearm_autoglobals(zend_auto_global *auto_global TSRMLS_DC) {
+ if (auto_global->name_len != sizeof("GLOBALS") - 1 || memcmp(auto_global->name, "GLOBALS", sizeof("GLOBALS") - 1)) {
+ auto_global->armed = 0;
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+typedef struct {
+ HashTable *ht[2];
+ HashPosition pos[2];
+} phpdbg_intersect_ptr;
+
+static int phpdbg_array_data_compare(const void *a, const void *b TSRMLS_DC) {
+ Bucket *f, *s;
+ zval result;
+ zval *first, *second;
+
+ f = *((Bucket **) a);
+ s = *((Bucket **) b);
+
+ first = *((zval **) f->pData);
+ second = *((zval **) s->pData);
+
+ if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
+ return 0;
+ }
+
+ if (Z_LVAL(result) < 0) {
+ return -1;
+ } else if (Z_LVAL(result) > 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void phpdbg_array_intersect_init(phpdbg_intersect_ptr *info, HashTable *ht1, HashTable *ht2 TSRMLS_DC) {
+ info->ht[0] = ht1;
+ info->ht[1] = ht2;
+
+ zend_hash_sort(info->ht[0], zend_qsort, (compare_func_t) phpdbg_array_data_compare, 0 TSRMLS_CC);
+ zend_hash_sort(info->ht[1], zend_qsort, (compare_func_t) phpdbg_array_data_compare, 0 TSRMLS_CC);
+
+ zend_hash_internal_pointer_reset_ex(info->ht[0], &info->pos[0]);
+ zend_hash_internal_pointer_reset_ex(info->ht[1], &info->pos[1]);
+}
+
+/* -1 => first array, 0 => both arrays equal, 1 => second array */
+static int phpdbg_array_intersect(phpdbg_intersect_ptr *info, zval ***ptr) {
+ int ret;
+ zval **zvpp[2];
+ int invalid = !info->ht[0] + !info->ht[1];
+
+ if (invalid > 0) {
+ invalid = !info->ht[0];
+
+ if (zend_hash_get_current_data_ex(info->ht[invalid], (void **) ptr, &info->pos[invalid]) == FAILURE) {
+ *ptr = NULL;
+ return 0;
+ }
+
+ zend_hash_move_forward_ex(info->ht[invalid], &info->pos[invalid]);
+
+ return invalid ? 1 : -1;
+ }
+
+ if (zend_hash_get_current_data_ex(info->ht[0], (void **) &zvpp[0], &info->pos[0]) == FAILURE) {
+ info->ht[0] = NULL;
+ return phpdbg_array_intersect(info, ptr);
+ }
+ if (zend_hash_get_current_data_ex(info->ht[1], (void **) &zvpp[1], &info->pos[1]) == FAILURE) {
+ info->ht[1] = NULL;
+ return phpdbg_array_intersect(info, ptr);
+ }
+
+ ret = zend_binary_zval_strcmp(*zvpp[0], *zvpp[1]);
+
+ if (ret <= 0) {
+ *ptr = zvpp[0];
+ zend_hash_move_forward_ex(info->ht[0], &info->pos[0]);
+ }
+ if (ret >= 0) {
+ *ptr = zvpp[1];
+ zend_hash_move_forward_ex(info->ht[1], &info->pos[1]);
+ }
+
+ return ret;
+}
+
+void phpdbg_webdata_decompress(char *msg, int len TSRMLS_DC) {
+#ifdef HAVE_JSON
+ zval *free_zv = NULL;
+ zval zv, **zvpp;
+ HashTable *ht;
+ php_json_decode(&zv, msg, len, 1, 1000 /* enough */ TSRMLS_CC);
+
+ if (JSON_G(error_code) != PHP_JSON_ERROR_NONE) {
+ phpdbg_error("wait", "type=\"invaliddata\" import=\"fail\"", "Malformed JSON was sent to this socket, arborting");
+ return;
+ }
+
+ ht = Z_ARRVAL(zv);
+
+ /* Reapply symbol table */
+ if (zend_hash_find(ht, "GLOBALS", sizeof("GLOBALS"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
+ {
+ zval **srv;
+ if (zend_hash_find(Z_ARRVAL_PP(zvpp), "_SERVER", sizeof("_SERVER"), (void **) &srv) == SUCCESS && Z_TYPE_PP(srv) == IS_ARRAY) {
+ zval **script;
+ if (zend_hash_find(Z_ARRVAL_PP(srv), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &script) == SUCCESS && Z_TYPE_PP(script) == IS_STRING) {
+ phpdbg_param_t param;
+ param.str = Z_STRVAL_PP(script);
+ PHPDBG_COMMAND_HANDLER(exec)(&param TSRMLS_CC);
+ }
+ }
+ }
+
+ PG(auto_globals_jit) = 0;
+ zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_dearm_autoglobals TSRMLS_CC);
+
+ zend_hash_clean(&EG(symbol_table));
+ EG(symbol_table) = *Z_ARRVAL_PP(zvpp);
+
+ /* Rebuild cookies, env vars etc. from GLOBALS (PG(http_globals)) */
+ phpdbg_rebuild_http_globals_array(TRACK_VARS_POST, "_POST" TSRMLS_CC);
+ phpdbg_rebuild_http_globals_array(TRACK_VARS_GET, "_GET" TSRMLS_CC);
+ phpdbg_rebuild_http_globals_array(TRACK_VARS_COOKIE, "_COOKIE" TSRMLS_CC);
+ phpdbg_rebuild_http_globals_array(TRACK_VARS_SERVER, "_SERVER" TSRMLS_CC);
+ phpdbg_rebuild_http_globals_array(TRACK_VARS_ENV, "_ENV" TSRMLS_CC);
+ phpdbg_rebuild_http_globals_array(TRACK_VARS_FILES, "_FILES" TSRMLS_CC);
+
+ Z_ADDREF_PP(zvpp);
+ free_zv = *zvpp;
+ }
+
+ if (zend_hash_find(ht, "input", sizeof("input"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_STRING) {
+ if (SG(request_info).request_body) {
+ php_stream_close(SG(request_info).request_body);
+ }
+ SG(request_info).request_body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
+ php_stream_truncate_set_size(SG(request_info).request_body, 0);
+ php_stream_write(SG(request_info).request_body, Z_STRVAL_PP(zvpp), Z_STRLEN_PP(zvpp));
+ }
+
+ if (zend_hash_find(ht, "cwd", sizeof("cwd"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_STRING) {
+ if (VCWD_CHDIR(Z_STRVAL_PP(zvpp)) == SUCCESS) {
+ if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) {
+ efree(BG(CurrentStatFile));
+ BG(CurrentStatFile) = NULL;
+ }
+ if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) {
+ efree(BG(CurrentLStatFile));
+ BG(CurrentLStatFile) = NULL;
+ }
+ }
+ }
+
+ if (zend_hash_find(ht, "sapi_name", sizeof("sapi_name"), (void **) &zvpp) == SUCCESS && (Z_TYPE_PP(zvpp) == IS_STRING || Z_TYPE_PP(zvpp) == IS_NULL)) {
+ if (PHPDBG_G(sapi_name_ptr)) {
+ free(PHPDBG_G(sapi_name_ptr));
+ }
+ if (Z_TYPE_PP(zvpp) == IS_STRING) {
+ PHPDBG_G(sapi_name_ptr) = sapi_module.name = strdup(Z_STRVAL_PP(zvpp));
+ } else {
+ PHPDBG_G(sapi_name_ptr) = sapi_module.name = NULL;
+ }
+ }
+
+ if (zend_hash_find(ht, "modules", sizeof("modules"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
+ HashPosition position;
+ phpdbg_intersect_ptr pos;
+ zval **module;
+ zend_module_entry *mod;
+ HashTable zv_registry;
+
+ /* intersect modules, unregister modules loaded "too much", announce not yet registered modules (phpdbg_notice) */
+
+ zend_hash_init(&zv_registry, zend_hash_num_elements(&module_registry), 0, ZVAL_PTR_DTOR, 0);
+ for (zend_hash_internal_pointer_reset_ex(&module_registry, &position);
+ zend_hash_get_current_data_ex(&module_registry, (void **) &mod, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&module_registry, &position)) {
+ if (mod->name) {
+ zval **value = emalloc(sizeof(zval *));
+ MAKE_STD_ZVAL(*value);
+ ZVAL_STRING(*value, mod->name, 1);
+ zend_hash_next_index_insert(&zv_registry, value, sizeof(zval *), NULL);
+ }
+ }
+
+ phpdbg_array_intersect_init(&pos, &zv_registry, Z_ARRVAL_PP(zvpp) TSRMLS_CC);
+ do {
+ int mode = phpdbg_array_intersect(&pos, &module);
+ if (mode < 0) {
+ // loaded module, but not needed
+ if (strcmp(PHPDBG_NAME, Z_STRVAL_PP(module))) {
+ zend_hash_del(&module_registry, Z_STRVAL_PP(module), Z_STRLEN_PP(module) + 1);
+ }
+ } else if (mode > 0) {
+ // not loaded module
+ if (!sapi_module.name || strcmp(sapi_module.name, Z_STRVAL_PP(module))) {
+ phpdbg_notice("wait", "missingmodule=\"%.*s\"", "The module %.*s isn't present in " PHPDBG_NAME ", you still can load via dl /path/to/module/%.*s.so", Z_STRLEN_PP(module), Z_STRVAL_PP(module), Z_STRLEN_PP(module), Z_STRVAL_PP(module));
+ }
+ }
+ } while (module);
+
+ zend_hash_clean(&zv_registry);
+ }
+
+ if (zend_hash_find(ht, "extensions", sizeof("extensions"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
+ zend_extension *extension;
+ zend_llist_position pos;
+ HashPosition hpos;
+ zval **name, key;
+
+ extension = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &pos);
+ while (extension) {
+ extension = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &pos);
+
+ /* php_serach_array() body should be in some ZEND_API function */
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &hpos);
+ zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void **) &name, &hpos) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &hpos)) {
+ if (Z_TYPE_PP(name) == IS_STRING && !zend_binary_strcmp(extension->name, strlen(extension->name), Z_STRVAL_PP(name), Z_STRLEN_PP(name))) {
+ break;
+ }
+ }
+
+ if (zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void **) &zvpp, &hpos) == FAILURE) {
+ /* sigh, breaking the encapsulation, there aren't any functions manipulating the llist at the place of the zend_llist_position */
+ zend_llist_element *elm = pos;
+ if (elm->prev) {
+ elm->prev->next = elm->next;
+ } else {
+ zend_extensions.head = elm->next;
+ }
+ if (elm->next) {
+ elm->next->prev = elm->prev;
+ } else {
+ zend_extensions.tail = elm->prev;
+ }
+#if ZEND_EXTENSIONS_SUPPORT
+ if (extension->shutdown) {
+ extension->shutdown(extension);
+ }
+#endif
+ if (zend_extensions.dtor) {
+ zend_extensions.dtor(elm->data);
+ }
+ pefree(elm, zend_extensions.persistent);
+ zend_extensions.count--;
+ } else {
+ zend_hash_get_current_key_zval_ex(Z_ARRVAL_PP(zvpp), &key, &hpos);
+ if (Z_TYPE(key) == IS_LONG) {
+ zend_hash_index_del(Z_ARRVAL_PP(zvpp), Z_LVAL(key));
+ }
+ }
+
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &hpos);
+ zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void **) &name, &hpos) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &hpos)) {
+ phpdbg_notice("wait", "missingextension=\"%.*s\"", "The Zend extension %.*s isn't present in " PHPDBG_NAME ", you still can load via dl /path/to/extension.so", Z_STRLEN_PP(name), Z_STRVAL_PP(name));
+ }
+ }
+ }
+
+ zend_ini_deactivate(TSRMLS_C);
+
+ if (zend_hash_find(ht, "systemini", sizeof("systemini"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
+ HashPosition position;
+ zval **ini_entry;
+ zend_ini_entry *original_ini;
+ zval key;
+
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &position);
+ zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void**) &ini_entry, &position) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &position)) {
+ zend_hash_get_current_key_zval_ex(Z_ARRVAL_PP(zvpp), &key, &position);
+ if (Z_TYPE(key) == IS_STRING) {
+ if (Z_TYPE_PP(ini_entry) == IS_STRING) {
+ if (zend_hash_find(EG(ini_directives), Z_STRVAL(key), Z_STRLEN(key) + 1, (void **) &original_ini) == SUCCESS) {
+ if (!original_ini->on_modify || original_ini->on_modify(original_ini, Z_STRVAL_PP(ini_entry), Z_STRLEN_PP(ini_entry), original_ini->mh_arg1, original_ini->mh_arg2, original_ini->mh_arg3, ZEND_INI_STAGE_ACTIVATE TSRMLS_CC) == SUCCESS) {
+ if (original_ini->modified && original_ini->orig_value != original_ini->value) {
+ efree(original_ini->value);
+ }
+ original_ini->value = Z_STRVAL_PP(ini_entry);
+ original_ini->value_length = Z_STRLEN_PP(ini_entry);
+ Z_TYPE_PP(ini_entry) = IS_NULL; /* don't free the value */
+ }
+ }
+ }
+ efree(Z_STRVAL(key));
+ }
+ }
+ }
+
+ if (zend_hash_find(ht, "userini", sizeof("userini"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
+ HashPosition position;
+ zval **ini_entry;
+ zval key;
+
+ for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &position);
+ zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void**) &ini_entry, &position) == SUCCESS;
+ zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &position)) {
+ zend_hash_get_current_key_zval_ex(Z_ARRVAL_PP(zvpp), &key, &position);
+ if (Z_TYPE(key) == IS_STRING) {
+ if (Z_TYPE_PP(ini_entry) == IS_STRING) {
+ zend_alter_ini_entry_ex(Z_STRVAL(key), Z_STRLEN(key) + 1, Z_STRVAL_PP(ini_entry), Z_STRLEN_PP(ini_entry), ZEND_INI_PERDIR, ZEND_INI_STAGE_HTACCESS, 1 TSRMLS_CC);
+ }
+ efree(Z_STRVAL(key));
+ }
+ }
+ }
+
+ zval_dtor(&zv);
+ if (free_zv) {
+ /* separate freeing to not dtor the symtable too, just the container zval... */
+ efree(free_zv);
+ }
+
+ /* Reapply raw input */
+ /* ??? */
+#endif
+}
+
+PHPDBG_COMMAND(wait) /* {{{ */
+{
+#ifdef HAVE_JSON
+ struct sockaddr_un local, remote;
+ int rlen, sr, sl;
+ unlink(PHPDBG_G(socket_path));
+ if (PHPDBG_G(socket_server_fd) == -1) {
+ int len;
+ PHPDBG_G(socket_server_fd) = sl = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ local.sun_family = AF_UNIX;
+ strcpy(local.sun_path, PHPDBG_G(socket_path));
+ len = strlen(local.sun_path) + sizeof(local.sun_family);
+ if (bind(sl, (struct sockaddr *)&local, len) == -1) {
+ phpdbg_error("wait", "type=\"nosocket\" import=\"fail\"", "Unable to connect to UNIX domain socket at %s defined by phpdbg.path ini setting", PHPDBG_G(socket_path));
+ return FAILURE;
+ }
+
+ chmod(PHPDBG_G(socket_path), 0666);
+
+ listen(sl, 2);
+ } else {
+ sl = PHPDBG_G(socket_server_fd);
+ }
+
+ rlen = sizeof(remote);
+ sr = accept(sl, (struct sockaddr *) &remote, (socklen_t *) &rlen);
+
+ char msglen[5];
+ int recvd = 4;
+
+ do {
+ recvd -= recv(sr, &(msglen[4 - recvd]), recvd, 0);
+ } while (recvd > 0);
+
+ recvd = *(size_t *) msglen;
+ char *data = emalloc(recvd);
+
+ do {
+ recvd -= recv(sr, &(data[(*(int *) msglen) - recvd]), recvd, 0);
+ } while (recvd > 0);
+
+ phpdbg_webdata_decompress(data, *(int *) msglen TSRMLS_CC);
+
+ if (PHPDBG_G(socket_fd) != -1) {
+ close(PHPDBG_G(socket_fd));
+ }
+ PHPDBG_G(socket_fd) = sr;
+
+ efree(data);
+
+ phpdbg_notice("wait", "import=\"success\"", "Successfully imported request data, stopped before executing");
+
+ return SUCCESS;
+#endif
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_wait.h b/sapi/phpdbg/phpdbg_wait.h
new file mode 100644
index 0000000000..7cf95919cc
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_wait.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WAIT_H
+#define PHPDBG_WAIT_H
+
+#include "zend.h"
+#include "phpdbg.h"
+
+PHPDBG_COMMAND(wait);
+
+void phpdbg_webdata_decompress(char *msg, int len TSRMLS_DC);
+
+#endif /* PHPDBG_WAIT_H */
diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c
new file mode 100644
index 0000000000..de6cddf5b3
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_watch.c
@@ -0,0 +1,715 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+#include "phpdbg_btree.h"
+#include "phpdbg_watch.h"
+#include "phpdbg_utils.h"
+#ifndef _WIN32
+# include <unistd.h>
+# include <sys/mman.h>
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+typedef struct {
+ void *page;
+ size_t size;
+ char reenable_writing;
+ /* data must be last element */
+ void *data;
+} phpdbg_watch_memdump;
+
+#define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
+
+
+static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) {
+ phpdbg_watchpoint_t *watch;
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ watch = result->ptr;
+
+ /* check if that addr is in a mprotect()'ed memory area */
+ if ((char *)phpdbg_get_page_boundary(watch->addr.ptr) > (char *)addr || (char *)phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *)addr) {
+ /* failure */
+ return NULL;
+ }
+
+ return watch;
+}
+
+static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access TSRMLS_DC) {
+ int m;
+
+ /* pagesize is assumed to be in the range of 2^x */
+ m = mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access);
+}
+
+static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_change_watchpoint_access(watch, PROT_READ TSRMLS_CC);
+}
+
+static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE TSRMLS_CC);
+}
+
+static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch);
+}
+
+static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr);
+}
+
+void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
+ watch->addr.ptr = addr;
+ watch->size = size;
+}
+
+void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
+ phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch);
+ watch->type = WATCH_ON_ZVAL;
+}
+
+void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
+ phpdbg_create_addr_watchpoint(ht, sizeof(HashTable), watch);
+ watch->type = WATCH_ON_HASHTABLE;
+}
+
+void phpdbg_watch_HashTable_dtor(zval **ptr);
+
+static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ watch->flags |= PHPDBG_WATCH_SIMPLE;
+
+ phpdbg_store_watchpoint(watch TSRMLS_CC);
+ zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL);
+
+ if (watch->type == WATCH_ON_ZVAL) {
+ phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong)watch->parent_container, watch->parent_container->pDestructor);
+ watch->parent_container->pDestructor = (dtor_func_t)phpdbg_watch_HashTable_dtor;
+ }
+
+ phpdbg_activate_watchpoint(watch TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ HashTable *ht;
+
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ default:
+ return FAILURE;
+ }
+
+ phpdbg_create_ht_watchpoint(ht, watch);
+
+ phpdbg_create_watchpoint(watch TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+static 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);
+ }
+
+ efree(tmp_watch->str);
+ efree(tmp_watch->name_in_parent);
+ efree(tmp_watch);
+
+ return ret;
+}
+
+static int phpdbg_watchpoint_parse_wrapper(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
+ int ret;
+ phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ watch->flags = 0;
+ watch->str = name;
+ watch->str_len = len;
+ watch->name_in_parent = keyname;
+ watch->name_in_parent_len = keylen;
+ watch->parent_container = parent;
+ phpdbg_create_zval_watchpoint(*zv, watch);
+
+ ret = callback(watch TSRMLS_CC);
+
+ if (ret != SUCCESS) {
+ efree(watch);
+ efree(name);
+ efree(keyname);
+ }
+
+ return ret;
+}
+
+PHPDBG_API 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) {
+ return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, 0, callback TSRMLS_CC);
+}
+
+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("watchdelete", "type=\"nowatch\"", "Nothing was deleted, no corresponding watchpoint found");
+ } else {
+ phpdbg_notice("watchdelete", "variable=\"%.*s\"", "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("watchrecursive", "variable=\"%.*s\"", "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("watcharray", "variable=\"%.*s\"", "Set array watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+void phpdbg_watch_HashTable_dtor(zval **zv) {
+ phpdbg_btree_result *result;
+ TSRMLS_FETCH();
+
+ 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("watchdelete", "variable=\"%.*s\" recursive=\"%s\"", "%.*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) {
+ phpdbg_watchpoint_t *watch = *(phpdbg_watchpoint_t **)pDest;
+ TSRMLS_FETCH();
+
+ phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
+ phpdbg_remove_watchpoint(watch TSRMLS_CC);
+
+ efree(watch->str);
+ efree(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, NULL, 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 ((size_t)watch->addr.ptr < (size_t)dump->page || (size_t)watch->addr.ptr + watch->size > (size_t) 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) {
+ zend_bool do_break = 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));
+
+ if (do_break) {
+ PHPDBG_G(watchpoint_hit) = 1;
+
+ phpdbg_notice("watchhit", "variable=\"%s\"", "Breaking on watchpoint %.*s", (int) watch->str_len, watch->str);
+ phpdbg_xml("<watchdata %r>");
+ }
+
+ 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) {
+ if ((Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) && removed) {
+ phpdbg_writeln("watchvalue", "type=\"old\" inaccessible=\"inaccessible\"", "Old value inaccessible, array or object (HashTable) already destroyed");
+ } else {
+ phpdbg_out("Old value: ");
+ phpdbg_xml("<watchvalue %r type=\"old\">");
+ zend_print_flat_zval_r((zval *) oldPtr TSRMLS_CC);
+ phpdbg_xml("</watchvalue>");
+ phpdbg_out("\n");
+ }
+ }
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && (removed || show_ref)) {
+ phpdbg_write("watchrefcount", "type=\"old\" refcount=\"%d\" isref=\"%d\"", "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("watchdelete", "variable=\"%.*s\"", "Watchpoint %.*s was unset, removing watchpoint", (int) watch->str_len, 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_out("New value: ");
+ phpdbg_xml("<watchvalue %r type=\"new\">");
+ zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC);
+ phpdbg_xml("</watchvalue>");
+ phpdbg_out("\n");
+ }
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && show_ref) {
+ phpdbg_writeln("watchrefcount", "type=\"new\" refcount=\"%d\" isref=\"%d\"", "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:
+
+#if ZEND_DEBUG
+ if (watch->addr.ht->inconsistent) {
+ phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Watchpoint %.*s was unset, removing watchpoint", (int) watch->str_len, 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("watchsize", "removed=\"%d\"", "%d elements were removed from the array", elementDiff);
+ } else {
+ phpdbg_writeln("watchsize", "added=\"%d\"", "%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("watcharrayptr", "", "Internal pointer of array was changed");
+ }
+ break;
+ }
+
+ if (do_break) {
+ phpdbg_xml("</watchdata>");
+ }
+ }
+
+ 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;
+
+ phpdbg_xml("<watchlist %r>");
+
+ 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("watchvariable", "variable=\"%.*s\" on=\"%s\" type=\"%s\"", "%.*s (%s, %s)", (int) (*watch)->str_len, (*watch)->str, (*watch)->type == WATCH_ON_HASHTABLE ? "array" : "variable", (*watch)->flags == PHPDBG_WATCH_RECURSIVE ? "recursive" : "simple");
+ }
+
+ phpdbg_xml("</watchlist>");
+}
+
+void phpdbg_watch_efree(void *ptr) {
+ phpdbg_btree_result *result;
+ TSRMLS_FETCH();
+
+ result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong) ptr);
+
+ if (result) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+
+ if ((size_t)watch->addr.ptr + watch->size > (size_t) ptr) {
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+ }
+
+ PHPDBG_G(original_free_function)(ptr);
+}
diff --git a/sapi/phpdbg/phpdbg_watch.h b/sapi/phpdbg/phpdbg_watch.h
new file mode 100644
index 0000000000..5c0079057c
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_watch.h
@@ -0,0 +1,112 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WATCH_H
+#define PHPDBG_WATCH_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#ifdef _WIN32
+# include "phpdbg_win.h"
+#endif
+
+#define PHPDBG_WATCH(name) PHPDBG_COMMAND(watch_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_WATCH(array);
+PHPDBG_WATCH(delete);
+PHPDBG_WATCH(recursive);
+
+/**
+ * Commands
+ */
+
+static const phpdbg_command_t phpdbg_watch_commands[] = {
+ PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s", 0),
+ PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s", 0),
+ PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s", 0),
+ PHPDBG_END_COMMAND
+};
+
+/* Watchpoint functions/typedefs */
+
+typedef enum {
+ WATCH_ON_ZVAL,
+ WATCH_ON_HASHTABLE,
+} phpdbg_watchtype;
+
+
+#define PHPDBG_WATCH_SIMPLE 0x0
+#define PHPDBG_WATCH_RECURSIVE 0x1
+
+typedef struct _phpdbg_watchpoint_t phpdbg_watchpoint_t;
+
+struct _phpdbg_watchpoint_t {
+ phpdbg_watchpoint_t *parent;
+ HashTable *parent_container;
+ char *name_in_parent;
+ size_t name_in_parent_len;
+ char *str;
+ size_t str_len;
+ union {
+ zval *zv;
+ HashTable *ht;
+ void *ptr;
+ } addr;
+ size_t size;
+ phpdbg_watchtype type;
+ char flags;
+};
+
+void phpdbg_setup_watchpoints(TSRMLS_D);
+
+#ifndef _WIN32
+int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC);
+#else
+int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC);
+#endif
+
+void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch);
+void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch);
+
+int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC);
+int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC);
+
+int phpdbg_print_changed_zvals(TSRMLS_D);
+
+void phpdbg_list_watchpoints(TSRMLS_D);
+
+void phpdbg_watch_efree(void *ptr);
+
+
+static long phpdbg_pagesize;
+
+static zend_always_inline void *phpdbg_get_page_boundary(void *addr) {
+ return (void *)((size_t)addr & ~(phpdbg_pagesize - 1));
+}
+
+static zend_always_inline size_t phpdbg_get_total_page_size(void *addr, size_t size) {
+ return (size_t)phpdbg_get_page_boundary((void *)((size_t)addr + size - 1)) - (size_t)phpdbg_get_page_boundary(addr) + phpdbg_pagesize;
+}
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_webdata_transfer.c b/sapi/phpdbg/phpdbg_webdata_transfer.c
new file mode 100644
index 0000000000..e7438ea01a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_webdata_transfer.c
@@ -0,0 +1,185 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg_webdata_transfer.h"
+#include "ext/json/php_json.h"
+
+PHPDBG_API void phpdbg_webdata_compress(char **msg, int *len TSRMLS_DC) {
+#ifdef HAVE_JSON
+ smart_str buf = {0};
+ zval array;
+ HashTable *ht;
+ /* I really need to change that to an array of zvals... */
+ zval zv1 = {{0}}, *zvp1 = &zv1;
+ zval zv2 = {{0}}, *zvp2 = &zv2;
+ zval zv3 = {{0}}, *zvp3 = &zv3;
+ zval zv4 = {{0}}, *zvp4 = &zv4;
+ zval zv5 = {{0}}, *zvp5 = &zv5;
+ zval zv6 = {{0}}, *zvp6 = &zv6;
+ zval zv7 = {{0}}, *zvp7 = &zv7;
+ zval zv8 = {{0}}, *zvp8 = &zv8;
+
+ array_init(&array);
+ ht = Z_ARRVAL(array);
+
+ /* fetch superglobals */
+ {
+ zend_is_auto_global(ZEND_STRL("GLOBALS") TSRMLS_CC);
+ /* might be JIT */
+ zend_is_auto_global(ZEND_STRL("_ENV") TSRMLS_CC);
+ zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC);
+ zend_is_auto_global(ZEND_STRL("_REQUEST") TSRMLS_CC);
+ array_init(&zv1);
+ zend_hash_copy(Z_ARRVAL(zv1), &EG(symbol_table), NULL, (void *) NULL, sizeof(zval *));
+ Z_ARRVAL(zv1)->pDestructor = NULL; /* we're operating on a copy! Don't double free zvals */
+ zend_hash_del(Z_ARRVAL(zv1), "GLOBALS", sizeof("GLOBALS")); /* do not use the reference to itself in json */
+ zend_hash_add(ht, "GLOBALS", sizeof("GLOBALS"), &zvp1, sizeof(zval *), NULL);
+ }
+
+ /* save php://input */
+ {
+ php_stream *stream;
+ int len;
+ char *contents;
+
+ stream = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
+ if ((len = php_stream_copy_to_mem(stream, &contents, PHP_STREAM_COPY_ALL, 0)) > 0) {
+ ZVAL_STRINGL(&zv2, contents, len, 0);
+ } else {
+ ZVAL_EMPTY_STRING(&zv2);
+ }
+ Z_SET_REFCOUNT(zv2, 2);
+ zend_hash_add(ht, "input", sizeof("input"), &zvp2, sizeof(zval *), NULL);
+ }
+
+ /* change sapi name */
+ {
+ if (sapi_module.name) {
+ ZVAL_STRING(&zv6, sapi_module.name, 0);
+ } else {
+ Z_TYPE(zv6) = IS_NULL;
+ }
+ Z_SET_REFCOUNT(zv6, 2);
+ zend_hash_add(ht, "sapi_name", sizeof("sapi_name"), &zvp6, sizeof(zval *), NULL);
+ }
+
+ /* handle modules / extensions */
+ {
+ HashPosition position;
+ zend_module_entry *module;
+ zend_extension *extension;
+ zend_llist_position pos;
+
+ array_init(&zv7);
+ for (zend_hash_internal_pointer_reset_ex(&module_registry, &position);
+ zend_hash_get_current_data_ex(&module_registry, (void**) &module, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&module_registry, &position)) {
+ zval **value = emalloc(sizeof(zval *));
+ ALLOC_ZVAL(*value);
+ ZVAL_STRING(*value, module->name, 1);
+ zend_hash_next_index_insert(Z_ARRVAL(zv7), value, sizeof(zval *), NULL);
+ }
+ zend_hash_add(ht, "modules", sizeof("modules"), &zvp7, sizeof(zval *), NULL);
+
+ array_init(&zv8);
+ extension = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &pos);
+ while (extension) {
+ zval **value = emalloc(sizeof(zval *));
+ ALLOC_ZVAL(*value);
+ ZVAL_STRING(*value, extension->name, 1);
+ zend_hash_next_index_insert(Z_ARRVAL(zv8), value, sizeof(zval *), NULL);
+ extension = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &pos);
+ }
+ zend_hash_add(ht, "extensions", sizeof("extensions"), &zvp8, sizeof(zval *), NULL);
+ }
+
+ /* switch cwd */
+ if (SG(options) & SAPI_OPTION_NO_CHDIR) {
+ char *ret = NULL;
+ char path[MAXPATHLEN];
+
+#if HAVE_GETCWD
+ ret = VCWD_GETCWD(path, MAXPATHLEN);
+#elif HAVE_GETWD
+ ret = VCWD_GETWD(path);
+#endif
+ if (ret) {
+ ZVAL_STRING(&zv5, path, 1);
+ Z_SET_REFCOUNT(zv5, 1);
+ zend_hash_add(ht, "cwd", sizeof("cwd"), &zvp5, sizeof(zval *), NULL);
+ }
+ }
+
+ /* get system ini entries */
+ {
+ HashPosition position;
+ zend_ini_entry *ini_entry;
+
+ array_init(&zv3);
+ for (zend_hash_internal_pointer_reset_ex(EG(ini_directives), &position);
+ zend_hash_get_current_data_ex(EG(ini_directives), (void**) &ini_entry, &position) == SUCCESS;
+ zend_hash_move_forward_ex(EG(ini_directives), &position)) {
+ zval **value = emalloc(sizeof(zval *));
+ if (ini_entry->modified) {
+ if (!ini_entry->orig_value) {
+ efree(value);
+ continue;
+ }
+ ALLOC_ZVAL(*value);
+ ZVAL_STRINGL(*value, ini_entry->orig_value, ini_entry->orig_value_length, 1);
+ } else {
+ if (!ini_entry->value) {
+ efree(value);
+ continue;
+ }
+ ALLOC_ZVAL(*value);
+ ZVAL_STRINGL(*value, ini_entry->value, ini_entry->value_length, 1);
+ }
+ zend_hash_add(Z_ARRVAL(zv3), ini_entry->name, ini_entry->name_length, value, sizeof(zval *), NULL);
+ }
+ zend_hash_add(ht, "systemini", sizeof("systemini"), &zvp3, sizeof(zval *), NULL);
+ }
+
+ /* get perdir ini entries */
+ if (EG(modified_ini_directives)) {
+ HashPosition position;
+ zend_ini_entry *ini_entry;
+
+ array_init(&zv4);
+ for (zend_hash_internal_pointer_reset_ex(EG(modified_ini_directives), &position);
+ zend_hash_get_current_data_ex(EG(modified_ini_directives), (void**) &ini_entry, &position) == SUCCESS;
+ zend_hash_move_forward_ex(EG(modified_ini_directives), &position)) {
+ zval **value = emalloc(sizeof(zval *));
+ if (!ini_entry->value) {
+ efree(value);
+ continue;
+ }
+ ALLOC_ZVAL(*value);
+ ZVAL_STRINGL(*value, ini_entry->value, ini_entry->value_length, 1);
+ zend_hash_add(Z_ARRVAL(zv4), ini_entry->name, ini_entry->name_length, value, sizeof(zval *), NULL);
+ }
+ zend_hash_add(ht, "userini", sizeof("userini"), &zvp4, sizeof(zval *), NULL);
+ }
+
+ /* encode data */
+ php_json_encode(&buf, &array, 0 TSRMLS_CC);
+ *msg = buf.c;
+ *len = buf.len;
+ zval_dtor(&array);
+#endif
+}
diff --git a/sapi/phpdbg/phpdbg_webdata_transfer.h b/sapi/phpdbg/phpdbg_webdata_transfer.h
new file mode 100644
index 0000000000..d70175ad99
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_webdata_transfer.h
@@ -0,0 +1,27 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WEBDATA_TRANSFER_H
+#define PHPDBG_WEBDATA_TRANSFER_H
+
+#include "zend.h"
+#include "phpdbg.h"
+
+PHPDBG_API void phpdbg_webdata_compress(char **msg, int *len TSRMLS_DC);
+
+#endif /* PHPDBG_WEBDATA_TRANSFER_H */
diff --git a/sapi/phpdbg/phpdbg_win.c b/sapi/phpdbg/phpdbg_win.c
new file mode 100644
index 0000000000..b0cbdf267a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_win.c
@@ -0,0 +1,42 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+
+int mprotect(void *addr, size_t size, int protection) {
+ int var;
+ return (int)VirtualProtect(addr, size, protection == (PROT_READ | PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, &var);
+}
+
+int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp) {
+ EXCEPTION_RECORD *xr = xp->ExceptionRecord;
+ CONTEXT *xc = xp->ContextRecord;
+
+ if(xr->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
+ TSRMLS_FETCH();
+
+ if (phpdbg_watchpoint_segfault_handler((void *)xr->ExceptionInformation[1] TSRMLS_CC) == SUCCESS) {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
diff --git a/sapi/phpdbg/phpdbg_win.h b/sapi/phpdbg/phpdbg_win.h
new file mode 100644
index 0000000000..68c3052790
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_win.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2014 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WIN_H
+#define PHPDBG_WIN_H
+
+#include "winbase.h"
+#include "windows.h"
+#include "excpt.h"
+
+#define PROT_READ 1
+#define PROT_WRITE 2
+
+int mprotect(void *addr, size_t size, int protection);
+
+void phpdbg_win_set_mm_heap(zend_mm_heap *heap);
+
+int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp);
+
+#endif \ No newline at end of file
diff --git a/sapi/phpdbg/test.php b/sapi/phpdbg/test.php
new file mode 100644
index 0000000000..d93c81a89a
--- /dev/null
+++ b/sapi/phpdbg/test.php
@@ -0,0 +1,87 @@
+<?php
+if (isset($include)) {
+ include (sprintf("%s/web-bootstrap.php", dirname(__FILE__)));
+}
+
+$stdout = fopen("php://stdout", "w+");
+
+class phpdbg {
+ 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) {
+ $var = $x + 1;
+ $var += 2;
+ $var <<= 3;
+
+ $foo = function () {
+ echo "bar!\n";
+ };
+
+ $foo();
+
+ yield $var;
+}
+
+$dbg = new phpdbg();
+
+var_dump(
+ $dbg->isGreat("PHP Rocks!!"));
+
+foreach (test(1,2) as $gen)
+ continue;
+
+echo "it works!\n";
+
+if (isset($dump))
+ var_dump($_SERVER);
+
+function phpdbg_test_ob()
+{
+ echo 'Start';
+ ob_start();
+ echo 'Hello';
+ $b = ob_get_clean();
+ 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);
diff --git a/sapi/phpdbg/tests/commands/0001_basic.test b/sapi/phpdbg/tests/commands/0001_basic.test
new file mode 100644
index 0000000000..08aa9ab664
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0001_basic.test
@@ -0,0 +1,8 @@
+#######################################################
+# name: basic
+# purpose: check basic functionality of phpdbg console
+# expect: TEST::EXACT
+# options: -rr
+#######################################################
+# [Nothing to execute!]
+#######################################################
diff --git a/sapi/phpdbg/tests/commands/0002_set.test b/sapi/phpdbg/tests/commands/0002_set.test
new file mode 100644
index 0000000000..6a14a15adc
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0002_set.test
@@ -0,0 +1,21 @@
+#################################################
+# name: set
+# purpose: tests for set commands
+# expect: TEST::CISTRING
+# options: -rr
+#################################################
+# setting prompt color
+# setting error color
+# setting notice color
+# Failed to find breakpoint #0
+# [Oplog off]
+# opened oplog test.log
+# nothing
+#################################################
+set color prompt none
+set color error none
+set color notice none
+set prompt promot>
+set break 0
+set oplog
+set oplog test.log
diff --git a/sapi/phpdbg/tests/commands/0101_info.test b/sapi/phpdbg/tests/commands/0101_info.test
new file mode 100644
index 0000000000..397a45c992
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0101_info.test
@@ -0,0 +1,19 @@
+#################################################
+# name: info
+# purpose: test info commands
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[User Classes (%d)]
+#User Class test (3)
+#|---- in phpdbginit code on line %d
+#################################################
+<:
+class test {
+ public function testMethod(){}
+ private function testPrivateMethod(){}
+ protected function testProtectedMethod(){}
+}
+:>
+info classes
+q
diff --git a/sapi/phpdbg/tests/commands/0102_print.test b/sapi/phpdbg/tests/commands/0102_print.test
new file mode 100644
index 0000000000..c174564e07
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0102_print.test
@@ -0,0 +1,27 @@
+#################################################
+# name: print
+# purpose: test print commands
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[User Class: test (3 methods)]
+#L%d-%d test::testMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+# L%d-%d test::testPrivateMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+# L%d-%d test::testProtectedMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+#[User Method testMethod (1 ops)]
+# L%d-%d test::testMethod() %s
+# L%d %s ZEND_RETURN C%d <unused> <unused>
+#################################################
+<:
+class test {
+ public function testMethod(){}
+ private function testPrivateMethod(){}
+ protected function testProtectedMethod(){}
+}
+:>
+print class test
+print method test::testMethod
+q
diff --git a/sapi/phpdbg/tests/commands/0103_register.test b/sapi/phpdbg/tests/commands/0103_register.test
new file mode 100644
index 0000000000..703a12f771
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0103_register.test
@@ -0,0 +1,28 @@
+#################################################
+# name: register
+# purpose: test registration functions
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[Registered test_function]
+#array(5) {
+# [0]=>
+# int(1)
+# [1]=>
+# int(2)
+# [2]=>
+# int(3)
+# [3]=>
+# int(4)
+# [4]=>
+# int(5)
+#}
+#################################################
+<:
+function test_function() {
+ var_dump(func_get_args());
+}
+:>
+R test_function
+test_function 1 2 3 4 5
+q
diff --git a/sapi/phpdbg/tests/commands/0104_clean.test b/sapi/phpdbg/tests/commands/0104_clean.test
new file mode 100644
index 0000000000..d50903c479
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0104_clean.test
@@ -0,0 +1,15 @@
+#################################################
+# name: clean
+# purpose: test cleaning environment
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#Cleaning Execution Environment
+#Classes %d
+#Functions %d
+#Constants %d
+#Includes %d
+#[Nothing to execute!]
+#################################################
+clean
+quit
diff --git a/sapi/phpdbg/tests/commands/0105_clear.test b/sapi/phpdbg/tests/commands/0105_clear.test
new file mode 100644
index 0000000000..8ce1002491
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0105_clear.test
@@ -0,0 +1,18 @@
+#################################################
+# name: clear
+# purpose: test clearing breakpoints
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#Clearing Breakpoints
+#File%w%d
+#Functions%w%d
+#Methods%w%d
+#Oplines%w%d
+#File oplines%w%d
+#Function oplines%w%d
+#Method oplines%w%d
+#Conditionals%w%d
+#################################################
+clear
+quit
diff --git a/sapi/phpdbg/tests/commands/0106_compile.test b/sapi/phpdbg/tests/commands/0106_compile.test
new file mode 100644
index 0000000000..b4d801670b
--- /dev/null
+++ b/sapi/phpdbg/tests/commands/0106_compile.test
@@ -0,0 +1,18 @@
+#################################################
+# name: compile
+# purpose: test compiling code
+# expect: TEST::FORMAT
+# options: -rr
+#################################################
+#[Successful compilation of %s]
+#Hello World
+#[Script ended normally]
+#################################################
+<:
+define('OUT',
+ tempnam(null, "phpdbg"));
+file_put_contents(OUT, "<?php echo \"Hello World\"; ?>");
+phpdbg_exec(OUT);
+:>
+run
+quit
diff --git a/sapi/phpdbg/tests/run-tests.php b/sapi/phpdbg/tests/run-tests.php
new file mode 100644
index 0000000000..1cc31d815e
--- /dev/null
+++ b/sapi/phpdbg/tests/run-tests.php
@@ -0,0 +1,597 @@
+<?php
+namespace phpdbg\testing {
+
+ /*
+ * Workaround ...
+ */
+ if (!defined('DIR_SEP'))
+ define('DIR_SEP', '\\' . DIRECTORY_SEPARATOR);
+
+ /**
+ * TestConfigurationExceptions are thrown
+ * when the configuration prohibits tests executing
+ *
+ * @package phpdbg
+ * @subpackage testing
+ */
+ class TestConfigurationException extends \Exception {
+
+ /**
+ *
+ * @param array Tests confguration
+ * @param message Exception message
+ * @param ... formatting parameters
+ */
+ public function __construct() {
+ $argv = func_get_args();
+
+ if (count($argv)) {
+
+ $this->config = array_shift($argv);
+ $this->message = vsprintf(
+ array_shift($argv), $argv);
+ }
+ }
+ }
+
+ /**
+ *
+ * @package phpdbg
+ * @subpackage testing
+ */
+ class TestsConfiguration implements \ArrayAccess {
+
+ /**
+ *
+ * @param array basic configuration
+ * @param array argv
+ */
+ public function __construct($config, $cmd) {
+ $this->options = $config;
+ while (($key = array_shift($cmd))) {
+ switch (substr($key, 0, 1)) {
+ case '-': switch(substr($key, 1, 1)) {
+ case '-': {
+ $arg = substr($key, 2);
+ if (($e=strpos($arg, '=')) !== false) {
+ $key = substr($arg, 0, $e);
+ $value = substr($arg, $e+1);
+ } else {
+ $key = $arg;
+ $value = array_shift($cmd);
+ }
+
+ if (isset($key) && isset($value)) {
+ switch ($key) {
+ case 'phpdbg':
+ case 'width':
+ $this->options[$key] = $value;
+ break;
+
+ default: {
+ if (isset($config[$key])) {
+ if (is_array($config[$key])) {
+ $this->options[$key][] = $value;
+ } else {
+ $this->options[$key] = array($config[$key], $value);
+ }
+ } else {
+ $this->options[$key] = $value;
+ }
+ }
+ }
+
+ }
+ } break;
+
+ default:
+ $this->flags[] = substr($key, 1);
+ } break;
+ }
+ }
+
+ if (!is_executable($this->options['phpdbg'])) {
+ throw new TestConfigurationException(
+ $this->options, 'phpdbg could not be found at the specified path (%s)', $this->options['phpdbg']);
+ } else $this->options['phpdbg'] = realpath($this->options['phpdbg']);
+
+ $this->options['width'] = (integer) $this->options['width'];
+
+ /* display properly, all the time */
+ if ($this->options['width'] < 50) {
+ $this->options['width'] = 50;
+ }
+
+ /* calculate column widths */
+ $this->options['lwidth'] = ceil($this->options['width'] / 3);
+ $this->options['rwidth'] = ceil($this->options['width'] - $this->options['lwidth']) - 5;
+ }
+
+ public function hasFlag($flag) {
+ return in_array(
+ $flag, $this->flags);
+ }
+
+ public function offsetExists($offset) { return isset($this->options[$offset]); }
+ public function offsetGet($offset) { return $this->options[$offset]; }
+ public function offsetUnset($offset) { unset($this->options[$offset]); }
+ public function offsetSet($offset, $data) { $this->options[$offset] = $data; }
+
+ protected $options = array();
+ protected $flags = array();
+ }
+
+ /**
+ * Tests is the console programming API for the test suite
+ *
+ * @package phpdbg
+ * @subpackage testing
+ */
+ class Tests {
+
+ /**
+ * Construct the console object
+ *
+ * @param array basic configuration
+ * @param array command line
+ */
+ public function __construct(TestsConfiguration $config) {
+ $this->config = $config;
+
+ if ($this->config->hasFlag('help') ||
+ $this->config->hasFlag('h')) {
+ $this->showUsage();
+ exit;
+ }
+ }
+
+ /**
+ * Find valid paths as specified by configuration
+ *
+ */
+ public function findPaths($in = null) {
+ $paths = array();
+ $where = ($in != null) ? array($in) : $this->config['path'];
+
+ foreach ($where as $path) {
+ if ($path) {
+ if (is_dir($path)) {
+ $paths[] = $path;
+ foreach (scandir($path) as $child) {
+ if ($child != '.' && $child != '..') {
+ $paths = array_merge(
+ $paths, $this->findPaths("$path/$child"));
+ }
+ }
+ }
+ }
+ }
+
+ return $paths;
+ }
+
+ /**
+ *
+ * @param string the path to log
+ */
+ public function logPath($path) {
+ printf(
+ '%s [%s]%s',
+ str_repeat(
+ '-', $this->config['width'] - strlen($path)),
+ $path, PHP_EOL);
+ }
+
+ /**
+ *
+ * @param string the path to log
+ */
+ public function logPathStats($path) {
+ if (!isset($this->stats[$path])) {
+ return;
+ }
+
+ $total = array_sum($this->stats[$path]);
+
+ if ($total) {
+ @$this->totals[true] += $this->stats[$path][true];
+ @$this->totals[false] += $this->stats[$path][false];
+
+ $stats = @sprintf(
+ "%d/%d %%%d",
+ $this->stats[$path][true],
+ $this->stats[$path][false],
+ (100 / $total) * $this->stats[$path][true]);
+
+ printf(
+ '%s [%s]%s',
+ str_repeat(
+ ' ', $this->config['width'] - strlen($stats)),
+ $stats, PHP_EOL);
+
+ printf("%s%s", str_repeat('-', $this->config['width']+3), PHP_EOL);
+ printf("%s", PHP_EOL);
+ }
+ }
+
+ /**
+ *
+ */
+ public function logStats() {
+ $total = array_sum($this->totals);
+ $stats = @sprintf(
+ "%d/%d %%%d",
+ $this->totals[true],
+ $this->totals[false],
+ (100 / $total) * $this->totals[true]);
+ printf(
+ '%s [%s]%s',
+ str_repeat(
+ ' ', $this->config['width'] - strlen($stats)),
+ $stats, PHP_EOL);
+
+ }
+
+ /**
+ *
+ */
+ protected function showUsage() {
+ printf('usage: php %s [flags] [options]%s', $this->config['exec'], PHP_EOL);
+ printf('[options]:%s', PHP_EOL);
+ printf("\t--path\t\tadd a path to scan outside of tests directory%s", PHP_EOL);
+ printf("\t--width\t\tset line width%s", PHP_EOL);
+ printf("\t--options\toptions to pass to phpdbg%s", PHP_EOL);
+ printf("\t--phpdbg\tpath to phpdbg binary%s", PHP_EOL);
+ printf('[flags]:%s', PHP_EOL);
+ printf("\t-diff2stdout\t\twrite diff to stdout instead of files%s", PHP_EOL);
+ printf("\t-nodiff\t\tdo not write diffs on failure%s", PHP_EOL);
+ printf("\t-nolog\t\tdo not write logs on failure%s", PHP_EOL);
+ printf('[examples]:%s', PHP_EOL);
+ printf("\tphp %s --phpdbg=/usr/local/bin/phpdbg --path=/usr/src/phpdbg/tests --options -n%s",
+ $this->config['exec'], PHP_EOL);
+
+ }
+
+ /**
+ * Find valid tests at the specified path (assumed valid)
+ *
+ * @param string a valid path
+ */
+ public function findTests($path) {
+ $tests = array();
+
+ foreach (scandir($path) as $file) {
+ if ($file == '.' || $file == '..')
+ continue;
+
+ $test = sprintf('%s/%s', $path, $file);
+
+ if (preg_match('~\.test$~', $test)) {
+ $tests[] = new Test($this->config, $test);
+ }
+ }
+
+ return $tests;
+ }
+
+ /**
+ *
+ * @param Test the test to log
+ */
+ public function logTest($path, Test $test) {
+ @$this->stats[$path][($result=$test->getResult())]++;
+
+ printf(
+ "%-{$this->config['lwidth']}s %-{$this->config['rwidth']}s [%s]%s",
+ $test->name,
+ $test->purpose,
+ $result ? "PASS" : "FAIL",
+ PHP_EOL);
+
+ return $result;
+ }
+
+ protected $config;
+ }
+
+ class Test {
+ /*
+ * Expect exact line for line match
+ */
+ const EXACT = 0x00000001;
+
+ /*
+ * Expect strpos() !== false
+ */
+ const STRING = 0x00000010;
+
+ /*
+ * Expect stripos() !== false
+ */
+ const CISTRING = 0x00000100;
+
+ /*
+ * Formatted output
+ */
+ const FORMAT = 0x00001000;
+
+ /**
+ * Format specifiers
+ */
+ private static $format = array(
+ 'search' => array(
+ '%e',
+ '%s',
+ '%S',
+ '%a',
+ '%A',
+ '%w',
+ '%i',
+ '%d',
+ '%x',
+ '%f',
+ '%c',
+ '%t',
+ '%T'
+ ),
+ 'replace' => array(
+ DIR_SEP,
+ '[^\r\n]+',
+ '[^\r\n]*',
+ '.+',
+ '.*',
+ '\s*',
+ '[+-]?\d+',
+ '\d+',
+ '[0-9a-fA-F]+',
+ '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
+ '.',
+ '\t',
+ '\t+'
+ )
+ );
+
+ /**
+ * Constructs a new Test object given a specilized phpdbginit file
+ *
+ * @param array configuration
+ * @param string file
+ */
+ public function __construct(TestsConfiguration $config, $file) {
+ if (($handle = fopen($file, 'r'))) {
+ while (($line = fgets($handle))) {
+ $trim = trim($line);
+
+ switch (substr($trim, 0, 1)) {
+ case '#': if (($chunks = array_map('trim', preg_split('~:~', substr($trim, 1), 2)))) {
+ if (property_exists($this, $chunks[0])) {
+ switch ($chunks[0]) {
+ case 'expect': {
+ if ($chunks[1]) {
+ switch (strtoupper($chunks[1])) {
+ case 'TEST::EXACT':
+ case 'EXACT': { $this->expect = TEST::EXACT; } break;
+
+ case 'TEST::STRING':
+ case 'STRING': { $this->expect = TEST::STRING; } break;
+
+ case 'TEST::CISTRING':
+ case 'CISTRING': { $this->expect = TEST::CISTRING; } break;
+
+ case 'TEST::FORMAT':
+ case 'FORMAT': { $this->expect = TEST::FORMAT; } break;
+
+ default:
+ throw new TestConfigurationException(
+ $this->config, "unknown type of expectation (%s)", $chunks[1]);
+ }
+ }
+ } break;
+
+ default: {
+ $this->$chunks[0] = $chunks[1];
+ }
+ }
+ } else switch(substr($trim, 1, 1)) {
+ case '#': { /* do nothing */ } break;
+
+ default: {
+ $line = preg_replace(
+ "~(\r\n)~", "\n", substr($trim, 1));
+
+ $line = trim($line);
+
+ switch ($this->expect) {
+ case TEST::FORMAT:
+ $this->match[] = str_replace(
+ self::$format['search'],
+ self::$format['replace'], preg_quote($line));
+ break;
+
+ default: $this->match[] = $line;
+ }
+ }
+ }
+ } break;
+
+ default:
+ break 2;
+ }
+ }
+ fclose($handle);
+
+ $this->config = $config;
+ $this->file = $file;
+ }
+ }
+
+ /**
+ * Obvious!!
+ *
+ */
+ public function getResult() {
+ $options = sprintf('-i%s -nqb', $this->file);
+
+ if ($this->options) {
+ $options = sprintf(
+ '%s %s %s',
+ $options,
+ $this->config['options'],
+ $this->options
+ );
+ } else {
+ $options = sprintf(
+ '%s %s', $options, $this->config['options']
+ );
+ }
+
+ $result = `{$this->config['phpdbg']} {$options}`;
+
+ if ($result) {
+ foreach (preg_split('~(\r|\n)~', $result) as $num => $line) {
+ if (!$line && !isset($this->match[$num]))
+ continue;
+
+ switch ($this->expect) {
+ case TEST::EXACT: {
+ if (strcmp($line, $this->match[$num]) !== 0) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+
+ case TEST::STRING: {
+ if (strpos($line, $this->match[$num]) === false) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+
+ case TEST::CISTRING: {
+ if (stripos($line, $this->match[$num]) === false) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+
+ case TEST::FORMAT: {
+ $line = trim($line);
+ if (!preg_match("/^{$this->match[$num]}\$/s", $line)) {
+ $this->diff['wants'][$num] = &$this->match[$num];
+ $this->diff['gets'][$num] = $line;
+ }
+ } continue 2;
+ }
+ }
+ }
+
+ $this->writeLog($result);
+ $this->writeDiff();
+
+ return (count($this->diff) == 0);
+ }
+
+ /**
+ * Write diff to disk if configuration allows it
+ *
+ */
+ protected function writeDiff() {
+ if (count($this->diff['wants'])) {
+ if (!$this->config->hasFlag('nodiff')) {
+ if ($this->config->hasFlag('diff2stdout')) {
+ $difffile = "php://stdout";
+ file_put_contents($difffile, "====DIFF====\n");
+ } else {
+ $difffile = sprintf(
+ '%s/%s.diff',
+ dirname($this->file), basename($this->file));
+ }
+
+ if (($diff = fopen($difffile, 'w+'))) {
+
+ foreach ($this->diff['wants'] as $line => $want) {
+ $got = $this->diff['gets'][$line];
+
+ fprintf(
+ $diff, '(%d) -%s%s', $line+1, $want, PHP_EOL);
+ fprintf(
+ $diff, '(%d) +%s%s', $line+1, $got, PHP_EOL);
+ }
+
+ fclose($diff);
+ }
+ }
+ } else unlink($diff);
+ }
+
+ /**
+ * Write log to disk if configuration allows it
+ *
+ */
+ protected function writeLog($result = null) {
+ $log = sprintf(
+ '%s/%s.log',
+ dirname($this->file), basename($this->file));
+
+ if (count($this->diff) && $result) {
+ if (!in_array('nolog', $this->config['flags'])) {
+ @file_put_contents(
+ $log, $result);
+ }
+ } else unlink($log);
+ }
+
+ public $name;
+ public $purpose;
+ public $file;
+ public $options;
+ public $expect;
+
+ protected $match;
+ protected $diff;
+ protected $stats;
+ protected $totals;
+ }
+}
+
+namespace {
+ use \phpdbg\Testing\Test;
+ use \phpdbg\Testing\Tests;
+ use \phpdbg\Testing\TestsConfiguration;
+
+ $cwd = dirname(__FILE__);
+ $cmd = $_SERVER['argv'];
+
+ $retval = 0;
+
+ {
+ $config = new TestsConfiguration(array(
+ 'exec' => realpath(array_shift($cmd)),
+ 'phpdbg' => realpath(sprintf(
+ '%s/../phpdbg', $cwd
+ )),
+ 'path' => array(
+ realpath(dirname(__FILE__))
+ ),
+ 'flags' => array(),
+ 'width' => 75
+ ), $cmd);
+
+ $tests = new Tests($config);
+
+ foreach ($tests->findPaths() as $path) {
+ $tests->logPath($path);
+
+ foreach ($tests->findTests($path) as $test) {
+ $retval |= !$tests->logTest($path, $test);
+ }
+
+ $tests->logPathStats($path);
+ }
+
+ $tests->logStats();
+ }
+
+ die($retval);
+}
+?>
diff --git a/sapi/phpdbg/travis/ci.sh b/sapi/phpdbg/travis/ci.sh
new file mode 100755
index 0000000000..206b158b9b
--- /dev/null
+++ b/sapi/phpdbg/travis/ci.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env sh
+git clone https://github.com/php/php-src
+cd php-src
+git checkout $PHP
+cd sapi
+rm -rf phpdbg
+git clone https://github.com/krakjoe/phpdbg.git
+cd ../
+./buildconf --force
+./configure --disable-all --enable-phpdbg --enable-maintainer-zts
+make
diff --git a/sapi/phpdbg/web-bootstrap.php b/sapi/phpdbg/web-bootstrap.php
new file mode 100644
index 0000000000..7b8c5d30de
--- /dev/null
+++ b/sapi/phpdbg/web-bootstrap.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * The following file shows how to bootstrap phpdbg so that you can mock specific server environments
+ *
+ * eval include("web-bootstrap.php")
+ * exec index.php
+ * compile
+ * break ...
+ * run
+ */
+if (!defined('PHPDBG_BOOTSTRAPPED'))
+{
+ /* define these once */
+ define("PHPDBG_BOOTPATH", "/opt/php-zts/htdocs");
+ define("PHPDBG_BOOTSTRAP", "index.php");
+ define("PHPDBG_BOOTSTRAPPED", sprintf("/%s", PHPDBG_BOOTSTRAP));
+}
+
+/*
+ * Superglobals are JIT, phpdbg will not over-write whatever you set during bootstrap
+ */
+
+$_SERVER = array
+(
+ 'HTTP_HOST' => 'localhost',
+ 'HTTP_CONNECTION' => 'keep-alive',
+ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36',
+ 'HTTP_ACCEPT_ENCODING' => 'gzip,deflate,sdch',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
+ 'HTTP_COOKIE' => 'tz=Europe%2FLondon; __utma=1.347100075.1384196523.1384196523.1384196523.1; __utmc=1; __utmz=1.1384196523.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)',
+ 'PATH' => '/usr/local/bin:/usr/bin:/bin',
+ 'SERVER_SIGNATURE' => '<address>Apache/2.4.6 (Ubuntu) Server at phpdbg.com Port 80</address>',
+ 'SERVER_SOFTWARE' => 'Apache/2.4.6 (Ubuntu)',
+ 'SERVER_NAME' => 'localhost',
+ 'SERVER_ADDR' => '127.0.0.1',
+ 'SERVER_PORT' => '80',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
+ 'REQUEST_SCHEME' => 'http',
+ 'CONTEXT_PREFIX' => '',
+ 'CONTEXT_DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
+ 'SERVER_ADMIN' => '[no address given]',
+ 'SCRIPT_FILENAME' => sprintf(
+ '%s/%s', PHPDBG_BOOTPATH, PHPDBG_BOOTSTRAP
+ ),
+ 'REMOTE_PORT' => '47931',
+ 'GATEWAY_INTERFACE' => 'CGI/1.1',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_METHOD' => 'GET',
+ 'QUERY_STRING' => '',
+ 'REQUEST_URI' => PHPDBG_BOOTSTRAPPED,
+ 'SCRIPT_NAME' => PHPDBG_BOOTSTRAPPED,
+ 'PHP_SELF' => PHPDBG_BOOTSTRAPPED,
+ 'REQUEST_TIME' => time(),
+);
+
+$_GET = array();
+$_REQUEST = array();
+$_POST = array();
+$_COOKIE = array();
+$_FILES = array();
+
+chdir(PHPDBG_BOOTPATH);
diff --git a/sapi/phpdbg/xml.md b/sapi/phpdbg/xml.md
new file mode 100644
index 0000000000..0556fb2415
--- /dev/null
+++ b/sapi/phpdbg/xml.md
@@ -0,0 +1,759 @@
+phpdbg XML format
+=================
+
+Common attributes
+=================
+
+severity
+--------
+
+- indicates the genre of phpdbg system output
+- usually one of these values:
+ - normal
+ - notice
+ - error
+
+msgout
+------
+
+- text message output related to the xml data (e.g. &lt;intro severity="normal" help="help" msgout="To get help using phpdbg type &amp;quot;help&amp;quot; and press enter" />)
+
+req
+---
+
+- the request id, if one was passed to the last command (via -r %d, where %d is the id) (and the output is related to that message)
+
+file
+----
+
+- refers to a filename
+
+method
+------
+
+- format classname::methodname
+- refers to a method
+
+function
+--------
+
+- refers to a function
+
+symbol
+------
+
+- either function or method (is method if "::" are present)
+
+opline
+------
+
+- in hexadecimal format
+- refers to a specific pointer to a (zend_)op
+
+opcode
+------
+
+- refers to an opcode (ZEND_*)
+
+type
+----
+
+- general attribute for most errors, describes the genre of the error
+
+
+General tags
+============
+
+intro
+-----
+
+- appears on startup if -q flag wasn't provided as command line arg
+- before any input possibility
+- attributes may be spread over multiple tags
+- wrapped in &lt;intros> tag
+
+### attributes ###
+
+- version: current phpdbg version (as string)
+- help: command name for help
+- report: URL for bug reporting
+
+prompt
+------
+
+- msg tag contains the text prompt
+- indicates that a new command may be accepted
+
+phpdbg
+------
+
+- general text message output from phpdbg system
+
+stream
+------
+
+- any output by PHP itself (e.g. &lt;stream type="stdout">test&lt;/stream>)
+
+### attributes ###
+
+- type: stderr or stdout
+
+php
+---
+
+- php error output
+
+### attributes ###
+
+- msg: the error message
+
+
+General error tags
+==================
+
+command
+-------
+
+- general errors about commands
+
+### possible attributes ###
+
+- type
+ - toomanyargs: more arguments than allowed
+ - noarg: argument missing
+ - wrongarg: wrong type of argument (e.g. letters instead of integer)
+ - toofewargs: not enough arguments
+ - notfound: command (or subcommand) doesn't exist
+ - ambiguous: command was ambiguous
+ - invalidcommand: command input is totally invalid
+ - (nostack: should not happen: is an internal error)
+ - (emptystack: should not happen: is an internal error)
+- command: passed command
+- subcommand: passed subcommand (present if the error is related to the subcommand)
+- expected: count of expected arguments
+- got: type of argument for type "wrongarg"
+- num: if possible, information about which parameter had a wrong argument
+
+inactive
+--------
+
+- by type
+ - op_array: nothing was yet compiled (probably because no execution context set)
+ - symbol_table: no symbol table present (not yet initiailized or already destructed)
+ - noexec: not in execution
+ - memory_manager: using the native memory manager (malloc, free, realloc) instead of e.g. the Zend MM
+ - notfound: file not found
+ - nocontext: execution context was not set (or compilation had failed)
+ - isrunning: command requires no running script
+
+
+Commands
+========
+
+export
+------
+
+- tag: &lt;exportbreakpoint />
+- usually triggered by successful export command
+- may appear when cleaning to temporary store breakpoints
+- errors by type
+ - openfailure: could not create file
+
+### attributes ###
+
+- count: number of exported breakpoints
+
+break / info break
+------------------
+
+- General tag for breakpoint creation, deletion and hits is "&lt;breakpoint />"
+
+### possible attributes ###
+
+- id: the breakpoint id (if the leave command was executed, the id has the value "leave")
+- num: the nth opline of a function/method/file
+- add: has value "success"/"fail": a brekpoint was successfully/not added
+- pending: the breakpoint is waiting for resolving (e.g. a file opline on a not yet loaded file)
+- deleted: has value "success"/"fail": a breakpoint was successfully/not deleted
+- eval: the condition on conditional breakpoints
+- file
+- opline
+- opcode
+- symbol
+- function
+- method
+- line
+
+
+- listing breakpoints always in a container element "&lt;breakpoints>"
+ - Child nodes:
+ - function
+ - method
+ - file
+ - opline
+ - methodopline
+ - functionopline
+ - fileopline
+ - evalfunction
+ - evalfunctionopline
+ - evalmethod
+ - evalmethodopline
+ - evalfile
+ - evalopline
+ - eval
+ - opcode
+ - attributes:
+ - name: name of the symbol (function/method/file/opcode)
+ - disabled: empty value if enabled, non-empty if enabled
+
+- errors (by type)
+ - exists: the breakpoint already exists
+ - maxoplines: tries to break at an opline (usedoplinenum) higher than the number of present oplines (in maxoplinenum)
+ - nomethod: method doesn't exist
+ - internalfunction: one cannot break on an opline of an internal function
+ - notregular: tries to set a breakpoint in not a regular file
+ - (invalidparameter: should not happen: is an internal error)
+
+frame
+-----
+
+- General tag for frames is "&lt;frame>"
+- always has id attribute; if it only has id attribute, it just indicates current frame number, no other elements follow
+- may contain other elements (of type &lt;arg>) when contained in &lt;backtrace> tag
+- &lt;arg> always contains a &lt;stream> element, the value of the variable
+
+### possible attributes ###
+
+- id: the frame id, current frame has id 0 (frames with internal function calls have the same id than their called frame)
+- symbol ("{main}" is root frame)
+- file
+- line
+- internal: has value "internal" when being an internal function call (one cannot inspect that frame)
+
+- being an error: (by type)
+ - maxnum: tried to access a frame with a number heigher than existing (or &lt; 0)
+
+### attributes on &lt;arg> ###
+
+- variadic: has a non-empty value if the argument is variadic
+- name: variable name of parameter
+
+info (subcommands)
+------------------
+
+### break ###
+
+- See above ("break / info break")
+
+### files ###
+
+- lists included files
+- &lt;includedfileinfo num="" /> with num having an integer value, indicating the number of included files
+- &lt;includedfile name=""/>: one per file, with name being the file path of the included file
+
+### error ###
+
+- gets last error
+- &lt;lasterror error="" (file="" line="") />
+- error attribute contains the last error as a string, is empty if there's no last error
+
+### constants ###
+
+- &lt;constantinfo num="" /> with num having an integer value, indicating the number of (local or superglobal) variables
+- if info vars was used it'll have also one of these attributes:
+ - method
+ - function
+ - file
+ - opline
+- for each variable there is a &lt;constant> element
+- &lt;constant address="" refcount="" type="" name="" />
+ - address: pointer to zval (hexadecimal)
+ - refcount: refcount of zval
+ - type: the variable type (long, string, ...). If the value is "unknown", the other attributes are meaningless
+ - name: the name of the variable
+ - value: the value of primitive types (scalars) => string/int/bool/double
+ - length: if string, then the length of that string
+
+### vars / globals ###
+
+- &lt;variableinfo num="" /> with num having an integer value, indicating the number of (local or superglobal) variables
+- if info vars was used it'll have also one of these attributes:
+ - method
+ - function
+ - file
+ - opline
+- for each variable there is a &lt;variable> element
+- &lt;variable address="" refcount="" type="" name="" />
+ - address: pointer to zval (hexadecimal)
+ - refcount: refcount of zval
+ - type: the variable type (long, string, ...). If the value is "unknown", the other attributes are meaningless
+ - name: the name of the variable
+ - refstatus: empty if the zval is not a reference
+ - class: the class the object in the zval is an instance of
+ - resource: the type of the resource in the zval
+ - value: the value of primitive types (scalars) => string/int/bool/double
+ - length: if string, then the length of that string
+
+### literal ###
+
+- &lt;literalinfo num="" /> with num having an integer value, indicating the number of literals, optional arguments are:
+ - method
+ - function
+ - file
+ - opline
+- for each literal there is a &lt;literal> followed by a &lt;stream type="stdout"> which prints the value of the literal
+- &lt;literal id="" />: where id is the internal identifier of the literal
+
+### memory ###
+
+- Format:
+
+ &lt;meminfo />
+ &lt;current />
+ &lt;used mem="" />
+ &lt;real mem="" />
+ &lt;peak />
+ &lt;used mem="" />
+ &lt;real mem="" />
+
+- mem is an attribute whose value is a float. The memory is given in kilobytes (1 kB == 1024 bytes)
+
+### classes ###
+
+- &lt;classinfo num="" /> with num having an integer value, indicating the number of loaded user-defined classes
+- Each class is enumerated with first a &lt;class>, then an optional &lt;parents> container and then a &lt;classsource> element
+- The &lt;parents> container contains the &lt;class> elements of the parent of the last &lt;class> element.
+- &lt;class type="" flags="" name="" methodcount="" />
+ - type: either "User" or "Internal"
+ - flags: either "Interface", "Class" or "Abstract Class"
+- &lt;classsource /> where the class was defined, if there are no attributes, location is unknown, usually defined by
+ - file
+ - line
+
+### funcs ###
+
+- &lt;functioninfo num="" /> with num having an integer value, indicating the number of loaded user-defined functions
+- Each class is enumerated with first a &lt;function> and then a &lt;functionsource> element
+- &lt;function name="" />
+- &lt;functionsource /> where the function was defined, if there are no attributes, location is unknown, usually defined by
+ - file
+ - line
+
+list
+----
+
+- consists of &lt;line> elements wrapped in a &lt;list> container
+- &lt;list file=""> is the container element with file being the filename
+- &lt;line line="" code="" /> with value of code being the whole line of code in the line specified in the line attribute
+ - current: this attribute is set to "current" if that line is the line where the executor currently is
+
+print
+-----
+
+### without a subcommand ###
+
+- &lt;print> elements are wrapped in a &lt;printinfo> element
+- there may be a variable number of &lt;print> elements with a variable count of args inside the &lt;printinfo> element
+- possible args are:
+ - readline: yes/no - readline enabled or disabled
+ - libedit: yes/no - libedit enabled or disabled
+ - context: current executing context
+ - compiled: yes/no - are there actual compiled ops?
+ - stepping: @@ TODO (meaningless for now) @@
+ - quiet: on/off - should it always print the opline being currently executed?
+ - oplog: on/off - are oplines logged in a file?
+ - ops: number of opcodes in current executing context
+ - vars: number of compiled variables (CV)
+ - executing: yes/no - in executor?
+ - vmret: the return value of the last executed opcode
+ - default: continue
+ - 1: return from vm
+ - 2: enter stack frame
+ - 3: leave stack frame
+ - classes: number of classes
+ - functions: number of functions
+ - constants: number of constants
+ - includes: number of included files
+
+### with a subcommand ###
+
+- introduced by &lt;printinfo num="" /> (except for print opline) with num being the number of opcodes and one of these args:
+ - file
+ - method
+ - function
+ - class (then also type and flags attributes, see info classes command for their meanings)
+ - symbol (also type and flags attributes; here the value of flags is either "Method" or "Function")
+- if there is a class method, the methods are all wrapped in a &lt;printmethods> container
+- then comes a &lt;printoplineinfo type="" /> where type is either "User" or "Internal"
+- the &lt;printoplineinfo> has either a method or a function attribute
+- if the type is "Internal"
+ - there are no oplines, it's an internal method or function
+- if the type is "User"
+ - it has these attributes
+ - startline: the first line of code where the method or function is defined
+ - endline: the lastt line of code where the method or function is defined
+ - file: the file of code where the method or function is defined
+ - is followed by the oplines of that method or function (&lt;print> elements)
+- &lt;print line="%u" opline="%p" opcode="%s" op="%s" />
+- in case of print opline it emits a single &lt;opline line="" opline="" opcode="" op="" file="" />
+
+exec
+----
+
+- command executing and compiling a given file
+ - &lt;exec type="unset" context="" />: indicates unsetting of the old context
+ - &lt;exec type="unsetops" />: indicates unsetting of the old compiled opcodes
+ - &lt;exec type="unchanged" />: same execution context choosen again
+ - &lt;exec type="set" context="" />: indicates setting of the new context
+- errors by tag
+ - &lt;compile>
+ - openfailure: couldn't open file
+ - compilefailure: The file indicated in context couldn't be compiled
+ - &lt;exec>
+ - invalid: given context (attribute) is not matching a valid file or symlink
+ - notfound: given context (attribute) does not exist
+
+run / &lt;stop> tag
+-------------------
+
+- runs the script (set via exec command)
+- &lt;stop type="end" />: script execution ended normally
+- (error) &lt;stop type="bailout" /> the VM bailed out (usually because there was some error)
+- compile failures see under exec, errors, &lt;compile>
+
+step
+----
+
+- steps by one line or opcode (as defined via set stepping) default is one line
+- returns back to the executor
+
+continue
+--------
+
+- returns back to the executor
+
+until
+-----
+
+- temporarily disables all the breakpoints on that line until that line was left once
+- returns back to the executor
+
+finish
+------
+
+- temporarily disables all the breakpoints until the end of the current frame
+- returns back to the executor
+
+leave
+------
+
+- temporarily disables all the breakpoints past the end of the current frame and then stops
+- returns back to the executor
+
+back
+----
+
+- prints backtrace
+- see frame command
+
+ev
+--
+
+- eval()uates some code
+- output wrapped in &lt;eval> tags
+- output is here first a dump of xml tags (see "Variable Dump" section), then a dump wrapped in <stream> tags
+- if there's an error, the tag will be <eval>, instead of the usual <php> tag
+
+sh
+--
+
+- executes shell command
+- still pipes to stdout ... without wrapping &lt;stream> !!! (@@ TODO @@)
+
+source
+------
+
+- executes a file in .phpdbginit format
+- errors by type
+ - notfound: file not found
+
+register
+--------
+
+- registers a function to be used like a command
+- &lt;register function="" />: successfully registered function
+- errors by type
+ - notfound: no such function
+ - inuse: function already registered
+
+quit
+----
+
+- quits phpdbg
+- if successful connection will be closed...
+
+clean
+-----
+
+- cleans environment (basically a shutdown + new startup)
+- &lt;clean> tags wrapped in a &lt;cleaninfo> container
+- possible attributes of &lt;clean> tag
+ - classes: number of classes
+ - functions: number of functions
+ - constants: number of constants
+ - includes: number of included files
+
+clear
+-----
+
+- removes all breakpoints
+- &lt;clear> tags wrapped in a &lt;clearinfo> container
+- possible attributes of &lt;clear> tag (value is always the number of defined breakpoints of that type)
+ - files
+ - functions
+ - methods
+ - oplines
+ - fileoplines
+ - functionoplines
+ - methodoplines
+ - eval
+
+watch
+-----
+
+- watchpoints generally are identified by a variable (one may need to switch frames first...)
+- &lt;watch variable="" />, &lt;watchrecursive variable="" /> and &lt;watcharray variable="" /> (normal, array, recursive)
+- &lt;watch> if error, by type:
+ - undefined: tried to set a watchpoint on a not (yet) defined variable
+ - notiterable: element which is tried to be accessed as an object or array is nor array nor object
+ - invalidinput: generally malformed input
+- &lt;watchdelete variable="" />: when "watch delete" was used on a watchpoint
+- (error) &lt;watchdelete type="nowatch" />: that watchpoint doesn't exist, so couldn't be deleted
+- for hit watchpoints etc., see Other tags, &lt;watch*>
+- when using watch list, &lt;watchvariable> elements are wrapped in a &lt;watchlist> container
+ - &lt;watchvariable variable="" on="" type="" />
+ - variable: watched variable (may be a variable of another scope!)
+ - on: values are array or variable, depending on what is watched
+ - type: values are recursive or simple, depending on whether the watchpoint is checked recursively or not
+
+set
+---
+
+- a general error is type="wrongargs" where a wrong argument was passed to a subcommand; tag is then &lt;set*>
+
+### prompt ###
+
+- without other args, a &lt;setpromt str="" /> tag is emitted where the value of the str attribue is the value of the prompt
+- when there is another arg, the prompt is changed to that arg, no further xml answer
+
+### break ###
+
+- enables / disables a given breakpoint silently with no further xml answer
+- if the boolean switch is omitted, it emits current state in a &lt;setbreak id="" active="" /> where active is on or off
+- error with type="nobreak", when no breakpoint with the given id exists
+
+### breaks ###
+
+- generally enables / disables breakpoint functionality silently with no futher xml answer
+- if the boolean switch is omitted, it emits current state in a &lt;setbreaks active="" /> where active is on or off
+
+### color ###
+
+- sets the color on prompt, error or notices
+- &lt;setcolor type="" color="" code="" />: code is the color code of color, type is either:
+ - prompt
+ - error
+ - notice
+- errors by type:
+ - nocolor: color doesn't exist
+ - invalidtype: type wasn't one of the three allowed types
+
+### colors ###
+
+- generally enables / disables colors silently with no further xml answer
+- if the boolean switch is omitted, it emits current state in a &lt;setcolors active="" /> where active is on or off
+
+### oplog ###
+
+- sets oplog
+- (error) &lt;setoplog type="openfailure" file="" /> when it couldn't open the passed file path
+- &lt;setoplog type="closingold" /> is emitted when there was a previous open oplog (and a file is passed)
+- if no further argument is passed, it emits current state in a &lt;setoplog active="" /> where active is on or off
+
+### quiet ###
+
+- generally enables / disables quietness silently with no further xml answer
+- if the boolean switch is omitted, it emits current state in a &lt;setquiet active="" /> where active is on or off
+
+### setpping ###
+
+- sets stepping to either opcode or line (so a step command will either advance one op or one line)
+- if no further argument is passed, it emits current state in a &lt;setoplog type="" /> where active is opcode or line
+
+### refcount ###
+
+- generally enables / disables showing of refcount in watchpoint breaks silently with no further xml answer
+- if the boolean switch is omitted, it emits current state in a &lt;setrefcount active="" /> where active is on or off
+
+wait
+----
+
+- internally executes exec, so exec will output first (if binding to socket worked)
+
+### attributes ###
+
+- import: has value "success"/"fail"
+- missingmodule/missingextension: modules/extensions loaded in the target SAPI, but not in phpdbg
+
+### errors (by type) ###
+
+- nosocket: couldn't establish socket
+- invaliddata: invalid JSON passed to socket
+
+dl
+--
+
+- loads a module or Zend extension at a given path
+- if a relative path is passed, it's relative to the extension_dir ini setting
+
+### attributes ###
+
+- extensiontype: "Zend extension" or "module"
+- name: the extension name
+- path: the path where it was loaded
+
+### errors (by type) ###
+
+- unsupported: dynamic extension loading is unsupported
+- relpath: relative path given, but no extension_dir defined
+- unknown: general error with internal DL_LOAD() (for message see msg attribute)
+- wrongapi: wrong Zend engine version (apineeded / apiinstalled attributes give information about the API versions)
+- wrongbuild: unmatched build versions (buildneeded / buildinstalled attributes give information about the build versions)
+- registerfailure: registering module failed
+- startupfailure: couldn't startup Zend extension / module
+- initfailure: couldn't initialize module
+- nophpso: passed shared object is not a valid Zend extension nor module
+
+- errors may have the module or extension attribute when their name is already known at the point of failure
+
+
+Other tags
+==========
+
+&lt;signal>
+-----------
+
+- received caught signal
+
+### attributes ###
+
+- type: type of signal (e.g. SIGINT)
+
+### by type ###
+
+- SIGINT: interactive mode is entered...
+
+&lt;watch*>
+-----------
+
+- generally emitted on hit watchpoint
+- &lt;watchdelete variable="" />: when a variable was unset, the watchpoint is removed too
+- &lt;watchhit variable="" />: when ever a watched variable is changed, followed by a &lt;watchdata> container
+- &lt;watchdata> may contain
+ - for watchpoints on variables:
+ - each of these &lt;watch*> tags conatins a type attribute whose value is either "old" or "new")
+ - &lt;watchvalue type="" inaccessible="inaccessible" />: old value is inaccessible
+ - &lt;watchvalue type=""> may contain a &lt;stream> element which indicates the old/new (type attribute) value of the variable
+ - &lt;watchrefcount type="" refcount="" isref="" />: old/new (type attribute) refcount and isref, both numbers
+ - isref: if the value is 0, it's not a reference, else it is one
+ - for watchpoints on arrays:
+ - &lt;watchsize> inspects size variations of an array (the sum):
+ - removed: number of elements removed
+ - added: number of elements added
+ - &lt;watcharrayptr>: if this tag appears, the internal pointer of the array way changed
+
+&lt;signalsegv>
+---------------
+
+- generally emitted when data couldn't be fetched (e.g. by accessing inconsistent data); only used in hard interrupt mode
+- it might mean that data couldn't be fetched at all, or that only incomplete data was fetched (e.g. when a fixed number of following attributes are fetched, this tag will mark a stop of fetching if none or not all tags were printed)
+
+
+Variable Dump
+=============
+
+- all except property and element tags have a refstatus attribute, is set to non-empty if it's a reference
+
+object properties
+-----------------
+
+- wrapped in a property tag &lt;property name="" protection="">
+ - name: name of key
+ - protection: one of these three values: public / protected / private
+ - class: only present if protection attribute is set to "private", contains the name of the class to which the property belongs
+- if the property tag contains any serverity="error" attribute, there was some crucial error to read it, just skip it
+
+array elements
+--------------
+- wrapped in an element tag &lt;property name="" protection="">
+ - name: name of key
+- if the element tag contains any serverity="error" attribute, there was some crucial error to read it, jsut skip it
+
+int
+---
+
+- &lt;int refstatus="" value="" />
+ - value is the integer
+
+float
+-----
+
+- &lt;float refstatus="" value="" />
+ - value is the float
+
+bool
+----
+
+- &lt;bool refstatus="" value="" />
+ -value: true or false
+
+string
+------
+
+- &lt;string refstatus="" length="" value="" />
+ - length: length or string
+ - value: the string
+
+null
+----
+
+- &lt;null refstatus="" />
+
+array
+-----
+
+- &lt;array refstatus="" num="">
+ - num: number of elements
+ - contains &lt;element> tags
+
+object
+------
+
+- &lt;object refstatus="" class="" id="" num="">
+ - class: name of the class the object is an instance of (may be empty if unknown)
+ - id: id of the object
+ - num: number of properties
+ - contains &lt;property> tags
+
+resource
+--------
+
+- &lt;resource refstatus="" id="" type="" />
+ - id: resource id
+ - type: type of resource
+
+recursion
+---------
+
+- &lt;recursion />
+- if that tag appears, there's a recursive reference inside the value to be printed
diff --git a/sapi/phpdbg/zend_mm_structs.h b/sapi/phpdbg/zend_mm_structs.h
new file mode 100644
index 0000000000..ca64069e0f
--- /dev/null
+++ b/sapi/phpdbg/zend_mm_structs.h
@@ -0,0 +1,102 @@
+#ifndef ZEND_MM_STRUCTS_H
+#define ZEND_MM_STRUCTS_H
+
+/* structs and macros defined in Zend/zend_alloc.c
+ Needed for realizing watchpoints and sigsafe memory */
+
+#include "zend.h"
+
+#ifndef ZEND_MM_COOKIES
+# define ZEND_MM_COOKIES ZEND_DEBUG
+#endif
+
+#define ZEND_MM_CACHE 1
+#ifndef ZEND_MM_CACHE_STAT
+# define ZEND_MM_CACHE_STAT 0
+#endif
+
+typedef struct _zend_mm_block_info {
+#if ZEND_MM_COOKIES
+ size_t _cookie;
+#endif
+ size_t _size;
+ size_t _prev;
+} zend_mm_block_info;
+
+typedef struct _zend_mm_small_free_block {
+ zend_mm_block_info info;
+#if ZEND_DEBUG
+ unsigned int magic;
+#ifdef ZTS
+ THREAD_T thread_id;
+#endif
+#endif
+ struct _zend_mm_free_block *prev_free_block;
+ struct _zend_mm_free_block *next_free_block;
+} zend_mm_small_free_block;
+
+typedef struct _zend_mm_free_block {
+ zend_mm_block_info info;
+#if ZEND_DEBUG
+ unsigned int magic;
+#ifdef ZTS
+ THREAD_T thread_id;
+#endif
+#endif
+ struct _zend_mm_free_block *prev_free_block;
+ struct _zend_mm_free_block *next_free_block;
+
+ struct _zend_mm_free_block **parent;
+ struct _zend_mm_free_block *child[2];
+} zend_mm_free_block;
+
+#define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \
+ (zend_mm_free_block *) ((char *)&heap->free_buckets[index * 2] + \
+ sizeof(zend_mm_free_block *) * 2 - \
+ sizeof(zend_mm_small_free_block))
+
+#define ZEND_MM_REST_BUCKET(heap) \
+ (zend_mm_free_block *)((char *)&heap->rest_buckets[0] + \
+ sizeof(zend_mm_free_block *) * 2 - \
+ sizeof(zend_mm_small_free_block))
+
+#define ZEND_MM_NUM_BUCKETS (sizeof(size_t) << 3)
+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;
+ size_t real_size;
+ size_t real_peak;
+ size_t limit;
+ size_t size;
+ size_t peak;
+ size_t reserve_size;
+ void *reserve;
+ int overflow;
+ int internal;
+#if ZEND_MM_CACHE
+ unsigned int cached;
+ zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS];
+#endif
+ zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2];
+ zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS];
+ zend_mm_free_block *rest_buckets[2];
+ int rest_count;
+#if ZEND_MM_CACHE_STAT
+ struct {
+ int count;
+ int max_count;
+ int hit;
+ int miss;
+ } cache_stat[ZEND_MM_NUM_BUCKETS+1];
+#endif
+};
+
+#endif