summaryrefslogtreecommitdiff
path: root/sapi/phpdbg/phpdbg_cmd.c
diff options
context:
space:
mode:
authorBob Weinand <bobwei9@hotmail.com>2013-12-20 14:27:51 +0100
committerBob Weinand <bobwei9@hotmail.com>2013-12-20 14:27:51 +0100
commitcee72c13d7defa8a51c33e2b6ed2c73d85fd4c2a (patch)
treec64561a3e7385a22ac6d416fb3252208b3a1392c /sapi/phpdbg/phpdbg_cmd.c
parent436ca2d019ed3ec3183129f9a3a7e329446077e9 (diff)
parentef3e0119b8437c855049ee3a67b0317dce055669 (diff)
downloadphp-git-cee72c13d7defa8a51c33e2b6ed2c73d85fd4c2a.tar.gz
Merge branch 'master' of sapi/phpdbg into PHP-5.6
Including phpdbg.
Diffstat (limited to 'sapi/phpdbg/phpdbg_cmd.c')
-rw-r--r--sapi/phpdbg/phpdbg_cmd.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/sapi/phpdbg/phpdbg_cmd.c b/sapi/phpdbg/phpdbg_cmd.c
new file mode 100644
index 0000000000..9f052d6f6f
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_cmd.c
@@ -0,0 +1,669 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_set.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ switch (param->type) {
+ 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 STR_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 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;
+ }
+} /* }}} */
+
+PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+ zend_ulong hash = param->type;
+
+ switch (param->type) {
+ 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;
+ }
+
+ 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 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;
+ }
+ }
+ }
+ return 0;
+} /* }}} */
+
+PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
+{
+ char *p;
+ char b[PHPDBG_MAX_CMD];
+ int l=0;
+ enum states {
+ IN_BETWEEN,
+ IN_WORD,
+ IN_STRING
+ } state = IN_BETWEEN;
+ phpdbg_input_t **argv = NULL;
+
+ argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*));
+ (*argc) = 0;
+
+#define RESET_STATE() do { \
+ phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \
+ if (arg) { \
+ b[l]=0; \
+ arg->length = l; \
+ arg->string = estrndup(b, arg->length); \
+ arg->argv = NULL; \
+ arg->argc = 0; \
+ argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \
+ argv[(*argc)++] = arg; \
+ l = 0; \
+ } \
+ state = IN_BETWEEN; \
+} while (0)
+
+ for (p = buffer; *p != '\0'; p++) {
+ int c = (unsigned char) *p;
+ switch (state) {
+ case IN_BETWEEN:
+ if (isspace(c)) {
+ continue;
+ }
+ if (c == '"') {
+ state = IN_STRING;
+ continue;
+ }
+ state = IN_WORD;
+ b[l++]=c;
+ continue;
+
+ case IN_STRING:
+ if (c == '"') {
+ if (buffer[(p - buffer)-1] == '\\') {
+ b[l-1]=c;
+ continue;
+ }
+ RESET_STATE();
+ } else {
+ b[l++]=c;
+ }
+ continue;
+
+ case IN_WORD:
+ if (isspace(c)) {
+ RESET_STATE();
+ } else {
+ b[l++]=c;
+ }
+ continue;
+ }
+ }
+
+ switch (state) {
+ case IN_WORD: {
+ RESET_STATE();
+ } break;
+
+ case IN_STRING:
+ phpdbg_error(
+ "Malformed command line (unclosed quote) @ %ld: %s!",
+ (p - buffer)-1, &buffer[(p - buffer)-1]);
+ break;
+
+ case IN_BETWEEN:
+ break;
+ }
+
+ if ((*argc) == 0) {
+ /* not needed */
+ efree(argv);
+
+ /* to be sure */
+ return NULL;
+ }
+
+ return argv;
+} /* }}} */
+
+PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
+{
+ phpdbg_input_t *buffer = NULL;
+ char *cmd = NULL;
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
+ (buffered == NULL)) {
+ fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
+ }
+
+ if (buffered == NULL) {
+#ifndef HAVE_LIBREADLINE
+ char buf[PHPDBG_MAX_CMD];
+ if ((!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && !phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) ||
+ !fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
+ /* the user has gone away */
+ phpdbg_error("Failed to read console!");
+ PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
+ zend_bailout();
+ return NULL;
+ }
+
+ cmd = buf;
+#else
+ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ char buf[PHPDBG_MAX_CMD];
+ if (fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
+ cmd = buf;
+ } else cmd = NULL;
+ } else cmd = readline(phpdbg_get_prompt(TSRMLS_C));
+
+ if (!cmd) {
+ /* the user has gone away */
+ phpdbg_error("Failed to read console!");
+ PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
+ zend_bailout();
+ return NULL;
+ }
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ add_history(cmd);
+ }
+#endif
+ } else cmd = buffered;
+
+ /* allocate and sanitize buffer */
+ buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
+ if (!buffer) {
+ return NULL;
+ }
+
+ buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length);
+
+ /* store constant pointer to start of buffer */
+ buffer->start = (char* const*) buffer->string;
+
+ buffer->argv = phpdbg_read_argv(
+ buffer->string, &buffer->argc TSRMLS_CC);
+
+#ifdef PHPDBG_DEBUG
+ if (buffer->argc) {
+ int arg = 0;
+
+ while (arg < buffer->argc) {
+ phpdbg_debug(
+ "argv %d=%s", arg, buffer->argv[arg]->string);
+ arg++;
+ }
+ }
+#endif
+
+#ifdef HAVE_LIBREADLINE
+ if (!buffered && cmd &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
+ free(cmd);
+ }
+#endif
+
+ return buffer;
+ }
+
+ return NULL;
+} /* }}} */
+
+PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */
+{
+ if (argv) {
+ if (argc) {
+ int arg;
+ for (arg=0; arg<argc; arg++) {
+ phpdbg_destroy_input(
+ &argv[arg] TSRMLS_CC);
+ }
+ }
+ efree(argv);
+ }
+
+} /* }}} */
+
+PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
+{
+ if (*input) {
+ if ((*input)->string) {
+ efree((*input)->string);
+ }
+
+ phpdbg_destroy_argv(
+ (*input)->argv, (*input)->argc TSRMLS_CC);
+
+ efree(*input);
+ }
+} /* }}} */
+
+PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+{
+ int rc = FAILURE;
+
+ if (input->argc > 0) {
+ while (command && command->name && command->handler) {
+ if (((command->name_len == input->argv[0]->length) &&
+ (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) ||
+ (command->alias &&
+ (input->argv[0]->length == 1) &&
+ (command->alias == *input->argv[0]->string))) {
+
+ phpdbg_param_t param;
+
+ param.type = EMPTY_PARAM;
+
+ if (input->argc > 1) {
+ if (command->subs) {
+ phpdbg_input_t sub = *input;
+
+ sub.string += input->argv[0]->length;
+ sub.length -= input->argv[0]->length;
+
+ sub.string = phpdbg_trim(
+ sub.string, sub.length, &sub.length);
+
+ sub.argc--;
+ sub.argv++;
+
+ phpdbg_debug(
+ "trying sub commands in \"%s\" for \"%s\" with %d arguments",
+ command->name, sub.argv[0]->string, sub.argc-1);
+
+ if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) {
+ efree(sub.string);
+ return SUCCESS;
+ }
+
+ efree(sub.string);
+ }
+
+ /* no sub command found */
+ {
+ char *store = input->string;
+
+ input->string += input->argv[0]->length;
+ input->length -= input->argv[0]->length;
+
+ input->string = phpdbg_trim(
+ input->string, input->length, &input->length);
+
+ efree(store);
+ }
+
+ /* pass parameter on */
+ phpdbg_parse_param(
+ input->string,
+ input->length,
+ &param TSRMLS_CC);
+ }
+
+ phpdbg_debug(
+ "found command %s for %s with %d arguments",
+ command->name, input->argv[0]->string, input->argc-1);
+ {
+ int arg;
+ for (arg=1; arg<input->argc; arg++) {
+ phpdbg_debug(
+ "\t#%d: [%s=%zu]",
+ arg,
+ input->argv[arg]->string,
+ input->argv[arg]->length);
+ }
+ }
+
+ rc = command->handler(&param, input TSRMLS_CC);
+
+ /* only set last command when it is worth it! */
+ if ((rc != FAILURE) &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
+ PHPDBG_G(lcmd) = (phpdbg_command_t*) command;
+ phpdbg_clear_param(
+ &PHPDBG_G(lparam) TSRMLS_CC);
+ PHPDBG_G(lparam) = param;
+ }
+ break;
+ }
+ command++;
+ }
+ } else {
+ /* this should NEVER happen */
+ phpdbg_error(
+ "No function executed!!");
+ }
+
+ return rc;
+} /* }}} */
+