diff options
Diffstat (limited to 'sapi/phpdbg/phpdbg.c')
| -rw-r--r-- | sapi/phpdbg/phpdbg.c | 1699 | 
1 files changed, 1699 insertions, 0 deletions
| diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c new file mode 100644 index 0000000000..9c56b1d7bc --- /dev/null +++ b/sapi/phpdbg/phpdbg.c @@ -0,0 +1,1699 @@ +/* +   +----------------------------------------------------------------------+ +   | 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); +int phpdbg_startup_run = 0; + +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_FILE_PENDING], 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(file_sources)); +	zend_hash_destroy(&PHPDBG_G(registered)); +	zend_hash_destroy(&PHPDBG_G(watchpoints)); +	zend_llist_destroy(&PHPDBG_G(watchlist_mem)); + +	if (PHPDBG_G(buffer)) { +		efree(PHPDBG_G(buffer)); +		PHPDBG_G(buffer) = NULL; +	} + +	if (PHPDBG_G(exec)) { +		efree(PHPDBG_G(exec)); +		PHPDBG_G(exec) = NULL; +	} + +	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_next() +    instructs phpdbg to insert a breakpoint at the next opcode */ +static PHP_FUNCTION(phpdbg_break_next) +{ +	if (zend_parse_parameters_none() != SUCCESS) { +		return; +	} 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_break_file(string file, integer line) */ +static PHP_FUNCTION(phpdbg_break_file) +{ +    char    *file = NULL; +    int      flen = 0; +    long     line; +     +    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &file, &flen, &line) == FAILURE) { +        return; +    } + +    phpdbg_set_breakpoint_file(file, line TSRMLS_CC); +} /* }}} */ + +/* {{{ proto void phpdbg_break_method(string class, string method) */ +static PHP_FUNCTION(phpdbg_break_method) +{ +    char *class = NULL, +         *method = NULL; +    int clen = 0,  +        mlen = 0; + +    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &class, &clen, &method, &mlen) == FAILURE) { +    return; +    } + +    phpdbg_set_breakpoint_method(class, method TSRMLS_CC); +} /* }}} */ + +/* {{{ proto void phpdbg_break_function(string function) */ +static PHP_FUNCTION(phpdbg_break_function) +{ +    char *function = NULL; +    int   function_len; + +    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &function, &function_len) == FAILURE) { +           return; +    } + +    phpdbg_set_breakpoint_symbol(function, function_len 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_FILE_PENDING]); +	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_next_arginfo, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_file_arginfo, 0, 0, 2) +    ZEND_ARG_INFO(0, file) +    ZEND_ARG_INFO(0, line) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_method_arginfo, 0, 0, 2) +    ZEND_ARG_INFO(0, class) +    ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_function_arginfo, 0, 0, 1) +    ZEND_ARG_INFO(0, function) +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_next, phpdbg_break_next_arginfo) +	PHP_FE(phpdbg_break_file, phpdbg_break_file_arginfo) +	PHP_FE(phpdbg_break_method, phpdbg_break_method_arginfo) +	PHP_FE(phpdbg_break_function, phpdbg_break_function_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_STOPPING)); + +		} +	} else fprintf(stdout, "%s\n", message); +} +/* }}} */ + +static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */ +{ +	if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) { +		zend_phpdbg_globals *pg = PHPDBG_G(backup) = calloc(1, sizeof(zend_phpdbg_globals)); + +		php_phpdbg_globals_ctor(pg); + +		pg->exec = strndup(PHPDBG_G(exec), PHPDBG_G(exec_len)); +		pg->exec_len = PHPDBG_G(exec_len); +		pg->oplog = PHPDBG_G(oplog); +		pg->prompt[0] = PHPDBG_G(prompt)[0]; +		pg->prompt[1] = PHPDBG_G(prompt)[1]; +		memcpy(pg->colors, PHPDBG_G(colors), sizeof(pg->colors)); +		pg->eol = PHPDBG_G(eol); +		pg->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK; +	} + +	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" +"output_buffering=off\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_startup_run == 0) { +		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_STOPPING; +			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 = NULL; +	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 = -1; +	zend_bool remote = 0; +	int step = 0; +	zend_phpdbg_globals *settings = NULL; + +#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; +	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; +	step = 0; +	sapi_name = NULL; +	if (settings) { +		exec = settings->exec; +	} + +	while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { +		switch (opt) { +			case 'r': +				phpdbg_startup_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 (!exec && (argc > php_optind) && (strcmp(argv[php_optind-1], "--") != SUCCESS)) { +		if (strlen(argv[php_optind])) { +			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; + +		/* set flags from command line */ +		PHPDBG_G(flags) = flags; + +		if (settings) { +#ifdef ZTS +			*((zend_phpdbg_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings; +#else +			phpdbg_globals = *settings; +#endif +		} + +		/* setup remote server if necessary */ +		if (cleaning <= 0 && 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 ? estrdup(exec) : 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; + +		/* 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(exec); + +			free(exec); +			exec = NULL; +		} + +		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 && cleaning < 2) { +			/* print blurb */ +			phpdbg_welcome(cleaning == 1 TSRMLS_CC); +		} + +		cleaning = -1; + +		/* initialize from file */ +		PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; +		zend_try { +			phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC); +			PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; +			phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC); +			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; +		} zend_end_try(); +		PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; +		 +		/* quit if init says so */ +		if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { +			goto phpdbg_out; +		} + +		/* auto compile */ +		if (PHPDBG_G(exec)) { +			if (settings) { +				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; +			} +			phpdbg_compile(TSRMLS_C); +			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; +		} + +		/* step from here, not through init */ +		if (step) { +			PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; +		} + +		if (phpdbg_startup_run) { +			zend_try { +				PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC); +			} zend_end_try(); +			if (phpdbg_startup_run > 1) { +				/* if -r is on the command line more than once just quit */ +				goto phpdbg_out; +			} +			phpdbg_startup_run = 0; +		} + +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_G(flags) |= PHPDBG_DISCARD_OUTPUT; +					phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC); +					PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; +					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 (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)); + + +		if (PHPDBG_G(exec) && (PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { +			exec = strdup(PHPDBG_G(exec)); /* preserve exec, don't reparse that from cmd */ +		} + +		/* 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..."); +	} +#endif + +		if (cleaning <= 0) { +			PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING; +			cleaning = -1; +		} + +		{ +			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; + +		if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) { +			php_free_shutdown_functions(TSRMLS_C); +			zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); +		} + +		/* sapi_module.deactivate is where to backup things, last chance before mm_shutdown... */ + +		zend_try { +			php_request_shutdown(NULL); +		} zend_end_try(); + +		if ((PHPDBG_G(flags) & (PHPDBG_IS_QUITTING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_RUNNING) { +			phpdbg_notice("stop", "type=\"normal\"", "Script ended normally"); +			cleaning++; +		} + +		if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) { +			settings = PHPDBG_G(backup); +		} + +		php_output_deactivate(TSRMLS_C); + +		zend_try { +			php_module_shutdown(TSRMLS_C); +		} zend_end_try(); + +		sapi_shutdown(); + +	} + +	if (cleaning > 0 || 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; +} /* }}} */ | 
