diff options
author | Ondřej Surý <ondrej@sury.org> | 2014-05-05 11:21:23 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2014-05-05 11:21:23 +0200 |
commit | 4bbffbee21093458feadd96f93b96d4627461cff (patch) | |
tree | d316b17d64aede352ae0a336c23238b8756004e6 /sapi/phpdbg/phpdbg_cmd.c | |
parent | 9566c3fcaf4cfaa866ea395ee5d1a480785fef0d (diff) | |
download | php-4bbffbee21093458feadd96f93b96d4627461cff.tar.gz |
New upstream version 5.6.0~beta2+dfsgupstream/5.6.0_beta2+dfsg
Diffstat (limited to 'sapi/phpdbg/phpdbg_cmd.c')
-rw-r--r-- | sapi/phpdbg/phpdbg_cmd.c | 674 |
1 files changed, 437 insertions, 237 deletions
diff --git a/sapi/phpdbg/phpdbg_cmd.c b/sapi/phpdbg/phpdbg_cmd.c index 1d78c5332..d4ce8ebc5 100644 --- a/sapi/phpdbg/phpdbg_cmd.c +++ b/sapi/phpdbg/phpdbg_cmd.c @@ -22,12 +22,32 @@ #include "phpdbg_cmd.h" #include "phpdbg_utils.h" #include "phpdbg_set.h" +#include "phpdbg_prompt.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); +static inline const char *phpdbg_command_name(const phpdbg_command_t *command, char *buffer) { + size_t pos = 0; + + if (command->parent) { + memcpy(&buffer[pos], command->parent->name, command->parent->name_len); + pos += command->parent->name_len; + memcpy(&buffer[pos], " ", sizeof(" ")-1); + pos += (sizeof(" ")-1); + } + + memcpy(&buffer[pos], command->name, command->name_len); + pos += command->name_len; + buffer[pos] = 0; + + return buffer; +} + PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ { switch (param->type) { + case STACK_PARAM: + return "stack"; case EMPTY_PARAM: return "empty"; case ADDR_PARAM: @@ -208,10 +228,19 @@ PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **point PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */ { switch ((dest->type = src->type)) { + case STACK_PARAM: + /* nope */ + break; + case STR_PARAM: dest->str = estrndup(src->str, src->len); dest->len = src->len; break; + + case OP_PARAM: + dest->str = estrndup(src->str, src->len); + dest->len = src->len; + break; case ADDR_PARAM: dest->addr = src->addr; @@ -226,6 +255,7 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des dest->method.name = estrdup(src->method.name); break; + case NUMERIC_FILE_PARAM: case FILE_PARAM: dest->file.name = estrdup(src->file.name); dest->file.line = src->file.line; @@ -246,6 +276,10 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des break; case EMPTY_PARAM: { /* do nothing */ } break; + + default: { + /* not yet */ + } } } /* }}} */ @@ -254,6 +288,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) / zend_ulong hash = param->type; switch (param->type) { + case STACK_PARAM: + /* nope */ + break; + case STR_PARAM: hash += zend_inline_hash_func(param->str, param->len); break; @@ -291,6 +329,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) / break; case EMPTY_PARAM: { /* do nothing */ } break; + + default: { + /* not yet */ + } } return hash; @@ -301,7 +343,11 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa if (l && r) { if (l->type == r->type) { switch (l->type) { - + case STACK_PARAM: + /* nope, or yep */ + return 1; + break; + case NUMERIC_FUNCTION_PARAM: if (l->num != r->num) { break; @@ -356,112 +402,400 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa case EMPTY_PARAM: return 1; + + default: { + /* not yet */ + } } } } return 0; } /* }}} */ -PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */ -{ - char *p; - char b[PHPDBG_MAX_CMD]; - int l=0; - enum states { - IN_BETWEEN, - IN_WORD, - IN_STRING - } state = IN_BETWEEN; - phpdbg_input_t **argv = NULL; - - argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*)); - (*argc) = 0; - -#define RESET_STATE() do { \ - phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \ - if (arg) { \ - b[l]=0; \ - arg->length = l; \ - arg->string = estrndup(b, arg->length); \ - arg->argv = NULL; \ - arg->argc = 0; \ - argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \ - argv[(*argc)++] = arg; \ - l = 0; \ - } \ - state = IN_BETWEEN; \ -} while (0) - - for (p = buffer; *p != '\0'; p++) { - int c = (unsigned char) *p; - switch (state) { - case IN_BETWEEN: - if (isspace(c)) { - continue; - } - if (c == '"') { - state = IN_STRING; - continue; - } - state = IN_WORD; - b[l++]=c; - continue; - - case IN_STRING: - if (c == '"') { - if (buffer[(p - buffer)-1] == '\\') { - b[l-1]=c; - continue; - } - RESET_STATE(); - } else { - b[l++]=c; - } - continue; +/* {{{ */ +PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) { + if (param && param->type) { + switch (param->type) { + case STR_PARAM: + fprintf(stderr, "%s STR_PARAM(%s=%lu)\n", msg, param->str, param->len); + break; + + case ADDR_PARAM: + fprintf(stderr, "%s ADDR_PARAM(%lu)\n", msg, param->addr); + break; + + case NUMERIC_FILE_PARAM: + fprintf(stderr, "%s NUMERIC_FILE_PARAM(%s:#%lu)\n", msg, param->file.name, param->file.line); + break; + + case FILE_PARAM: + fprintf(stderr, "%s FILE_PARAM(%s:%lu)\n", msg, param->file.name, param->file.line); + break; + + case METHOD_PARAM: + fprintf(stderr, "%s METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name); + break; + + case NUMERIC_METHOD_PARAM: + fprintf(stderr, "%s NUMERIC_METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name); + break; + + case NUMERIC_FUNCTION_PARAM: + fprintf(stderr, "%s NUMERIC_FUNCTION_PARAM(%s::%ld)\n", msg, param->str, param->num); + break; + + case NUMERIC_PARAM: + fprintf(stderr, "%s NUMERIC_PARAM(%ld)\n", msg, param->num); + break; + + case COND_PARAM: + fprintf(stderr, "%s COND_PARAM(%s=%lu)\n", msg, param->str, param->len); + break; + + case OP_PARAM: + fprintf(stderr, "%s OP_PARAM(%s=%lu)\n", msg, param->str, param->len); + break; + + default: { + /* not yet */ + } + } + } +} /* }}} */ - case IN_WORD: - if (isspace(c)) { - RESET_STATE(); - } else { - b[l++]=c; +/* {{{ */ +PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack) { + if (stack && stack->next) { + phpdbg_param_t *remove = stack->next; + + while (remove) { + phpdbg_param_t *next = NULL; + + if (remove->next) + next = remove->next; + + switch (remove->type) { + case NUMERIC_METHOD_PARAM: + case METHOD_PARAM: + if (remove->method.class) + free(remove->method.class); + if (remove->method.name) + free(remove->method.name); + break; + + case NUMERIC_FUNCTION_PARAM: + case STR_PARAM: + case OP_PARAM: + if (remove->str) + free(remove->str); + break; + + case NUMERIC_FILE_PARAM: + case FILE_PARAM: + if (remove->file.name) + free(remove->file.name); + break; + + default: { + /* nothing */ } - continue; + } + + free(remove); + remove = NULL; + + if (next) + remove = next; + else break; } } + + stack->next = NULL; +} /* }}} */ - switch (state) { - case IN_WORD: { - RESET_STATE(); - } break; +/* {{{ */ +PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) { + phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t)); - case IN_STRING: - phpdbg_error( - "Malformed command line (unclosed quote) @ %ld: %s!", - (p - buffer)-1, &buffer[(p - buffer)-1]); - break; + if (!next) + return; - case IN_BETWEEN: - break; + *(next) = *(param); + + next->next = NULL; + + if (stack->top == NULL) { + stack->top = next; + next->top = NULL; + stack->next = next; + } else { + stack->top->next = next; + next->top = stack->top; + stack->top = next; } - if ((*argc) == 0) { - /* not needed */ - efree(argv); + stack->len++; +} /* }}} */ - /* to be sure */ - return NULL; +PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC) { + if (command) { + char buffer[128] = {0,}; + const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL; + const char *arg = command->args; + size_t least = 0L, + received = 0L, + current = 0L; + zend_bool optional = 0; + + /* check for arg spec */ + if (!(arg) || !(*arg)) { + if (!top) { + return SUCCESS; + } + + asprintf(why, + "The command \"%s\" expected no arguments", + phpdbg_command_name(command, buffer)); + return FAILURE; + } + + least = 0L; + + /* count least amount of arguments */ + while (arg && *arg) { + if (arg[0] == '|') { + break; + } + least++; + arg++; + } + + arg = command->args; + +#define verify_arg(e, a, t) if (!(a)) { \ + if (!optional) { \ + asprintf(why, \ + "The command \"%s\" expected %s and got nothing at parameter %lu", \ + phpdbg_command_name(command, buffer), \ + (e), \ + current); \ + return FAILURE;\ + } \ +} else if ((a)->type != (t)) { \ + asprintf(why, \ + "The command \"%s\" expected %s and got %s at parameter %lu", \ + phpdbg_command_name(command, buffer), \ + (e),\ + phpdbg_get_param_type((a) TSRMLS_CC), \ + current); \ + return FAILURE; \ +} + + while (arg && *arg) { + current++; + + switch (*arg) { + case '|': { + current--; + optional = 1; + arg++; + } continue; + + case 'i': verify_arg("raw input", top, STR_PARAM); break; + case 's': verify_arg("string", top, STR_PARAM); break; + case 'n': verify_arg("number", top, NUMERIC_PARAM); break; + case 'm': verify_arg("method", top, METHOD_PARAM); break; + case 'a': verify_arg("address", top, ADDR_PARAM); break; + case 'f': verify_arg("file:line", top, FILE_PARAM); break; + case 'c': verify_arg("condition", top, COND_PARAM); break; + case 'o': verify_arg("opcode", top, OP_PARAM); break; + case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break; + + case '*': { /* do nothing */ } break; + } + + if (top ) { + top = top->next; + } else break; + + received++; + arg++; + } + +#undef verify_arg + + if ((received < least)) { + asprintf(why, + "The command \"%s\" expected at least %lu arguments (%s) and received %lu", + phpdbg_command_name(command, buffer), + least, + command->args, + received); + return FAILURE; + } } + + return SUCCESS; +} + +/* {{{ */ +PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why) { + const phpdbg_command_t *command = commands; + phpdbg_param_t *name = *top; + const phpdbg_command_t *matched[3] = {NULL, NULL, NULL}; + ulong matches = 0L; + + while (command && command->name && command->handler) { + if ((name->len == 1) || (command->name_len >= name->len)) { + /* match single letter alias */ + if (command->alias && (name->len == 1)) { + if (command->alias == (*name->str)) { + matched[matches] = command; + matches++; + } + } else { - return argv; + /* match full, case insensitive, command name */ + if (strncasecmp(command->name, name->str, name->len) == SUCCESS) { + if (matches < 3) { + + /* only allow abbreviating commands that can be aliased */ + if (((name->len != command->name_len) && command->alias) || + (name->len == command->name_len)) { + matched[matches] = command; + matches++; + } + + + /* exact match */ + if (name->len == command->name_len) + break; + } else break; + } + } + } + + command++; + } + + switch (matches) { + case 0: { + if (parent) { + asprintf( + why, + "The command \"%s %s\" could not be found", + parent->name, name->str); + } else asprintf( + why, + "The command \"%s\" could not be found", + name->str); + } return parent; + + case 1: { + (*top) = (*top)->next; + + command = matched[0]; + } break; + + default: { + char *list = NULL; + zend_uint it = 0; + size_t pos = 0; + + while (it < matches) { + if (!list) { + list = malloc( + matched[it]->name_len + 1 + + ((it+1) < matches ? sizeof(", ")-1 : 0)); + } else { + list = realloc(list, + (pos + matched[it]->name_len) + 1 + + ((it+1) < matches ? sizeof(", ")-1 : 0)); + } + memcpy(&list[pos], matched[it]->name, matched[it]->name_len); + pos += matched[it]->name_len; + if ((it+1) < matches) { + memcpy(&list[pos], ", ", sizeof(", ")-1); + pos += (sizeof(", ") - 1); + } + + list[pos] = 0; + it++; + } + + asprintf( + why, + "The command \"%s\" is ambigious, matching %lu commands (%s)", + name->str, matches, list); + free(list); + } return NULL; + } + + if (command->subs && (*top) && ((*top)->type == STR_PARAM)) { + return phpdbg_stack_resolve(command->subs, command, top, why); + } else { + return command; + } + + return NULL; } /* }}} */ -PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */ +/* {{{ */ +PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC) { + phpdbg_param_t *top = NULL; + const phpdbg_command_t *handler = NULL; + + if (stack->type != STACK_PARAM) { + asprintf( + why, "The passed argument was not a stack !!"); + return FAILURE; + } + + if (!stack->len) { + asprintf( + why, "The stack contains nothing !!"); + return FAILURE; + } + + top = (phpdbg_param_t*) stack->next; + + switch (top->type) { + case EVAL_PARAM: + return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC); + + case RUN_PARAM: + return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC); + + case SHELL_PARAM: + return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC); + + case STR_PARAM: { + handler = phpdbg_stack_resolve( + phpdbg_prompt_commands, NULL, &top, why); + + if (handler) { + if (phpdbg_stack_verify(handler, &top, why TSRMLS_CC) == SUCCESS) { + return handler->handler(top TSRMLS_CC); + } + } + } return FAILURE; + + default: + asprintf( + why, "The first parameter makes no sense !!"); + return FAILURE; + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */ { - phpdbg_input_t *buffer = NULL; char *cmd = NULL; #ifndef HAVE_LIBREADLINE char buf[PHPDBG_MAX_CMD]; #endif + char *buffer = NULL; if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && @@ -513,32 +847,8 @@ readline: } #endif } else cmd = buffered; - - /* allocate and sanitize buffer */ - buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t)); - if (!buffer) { - return NULL; - } - - buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length); - - /* store constant pointer to start of buffer */ - buffer->start = (char* const*) buffer->string; - - buffer->argv = phpdbg_read_argv( - buffer->string, &buffer->argc TSRMLS_CC); - -#ifdef PHPDBG_DEBUG - if (buffer->argc) { - int arg = 0; - - while (arg < buffer->argc) { - phpdbg_debug( - "argv %d=%s", arg, buffer->argv[arg]->string); - arg++; - } - } -#endif + + buffer = estrdup(cmd); #ifdef HAVE_LIBREADLINE if (!buffered && cmd && @@ -546,144 +856,34 @@ readline: free(cmd); } #endif - - return buffer; } - return NULL; -} /* }}} */ + if (buffer && isspace(*buffer)) { + char *trimmed = buffer; + while (isspace(*trimmed)) + trimmed++; -PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */ -{ - if (argv) { - if (argc) { - int arg; - for (arg=0; arg<argc; arg++) { - phpdbg_destroy_input( - &argv[arg] TSRMLS_CC); - } - } - efree(argv); + trimmed = estrdup(trimmed); + efree(buffer); + buffer = trimmed; } -} /* }}} */ - -PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */ -{ - if (*input) { - if ((*input)->string) { - efree((*input)->string); + if (buffer && strlen(buffer)) { + if (PHPDBG_G(buffer)) { + efree(PHPDBG_G(buffer)); + } + PHPDBG_G(buffer) = estrdup(buffer); + } else { + if (PHPDBG_G(buffer)) { + buffer = estrdup(PHPDBG_G(buffer)); } - - phpdbg_destroy_argv( - (*input)->argv, (*input)->argc TSRMLS_CC); - - efree(*input); } + + return buffer; } /* }}} */ -PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */ { - int rc = FAILURE; - - if (input->argc > 0) { - while (command && command->name && command->handler) { - if (((command->name_len == input->argv[0]->length) && - (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) || - (command->alias && - (input->argv[0]->length == 1) && - (command->alias == *input->argv[0]->string))) { - - phpdbg_param_t param; - phpdbg_command_t *initial_last_cmd; - phpdbg_param_t initial_last_param; - - param.type = EMPTY_PARAM; - - if (input->argc > 1) { - if (command->subs) { - phpdbg_input_t sub = *input; - - sub.string += input->argv[0]->length; - sub.length -= input->argv[0]->length; - - sub.string = phpdbg_trim( - sub.string, sub.length, &sub.length); - - sub.argc--; - sub.argv++; - - phpdbg_debug( - "trying sub commands in \"%s\" for \"%s\" with %d arguments", - command->name, sub.argv[0]->string, sub.argc-1); - - if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) { - efree(sub.string); - return SUCCESS; - } - - efree(sub.string); - } - - /* no sub command found */ - { - char *store = input->string; - - input->string += input->argv[0]->length; - input->length -= input->argv[0]->length; - - input->string = phpdbg_trim( - input->string, input->length, &input->length); - - efree(store); - } - - /* pass parameter on */ - phpdbg_parse_param( - input->string, - input->length, - ¶m TSRMLS_CC); - } - - phpdbg_debug( - "found command %s for %s with %d arguments", - command->name, input->argv[0]->string, input->argc-1); - { - int arg; - for (arg=1; arg<input->argc; arg++) { - phpdbg_debug( - "\t#%d: [%s=%zu]", - arg, - input->argv[arg]->string, - input->argv[arg]->length); - } - } - - initial_last_param = PHPDBG_G(lparam); - initial_last_cmd = (phpdbg_command_t *)PHPDBG_G(lcmd); - PHPDBG_G(lparam) = param; - PHPDBG_G(lcmd) = (phpdbg_command_t *)command; - - rc = command->handler(¶m, input TSRMLS_CC); - - /* only set last command when it is worth it! */ - if (rc != FAILURE && !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { - phpdbg_clear_param(&initial_last_param TSRMLS_CC); - } else if (PHPDBG_G(lcmd) == command && !memcmp(&PHPDBG_G(lparam),& initial_last_param, sizeof(phpdbg_param_t))) { - PHPDBG_G(lparam) = initial_last_param; - PHPDBG_G(lcmd) = initial_last_cmd; - phpdbg_clear_param(¶m TSRMLS_CC); - } - break; - } - command++; - } - } else { - /* this should NEVER happen */ - phpdbg_error( - "No function executed!!"); - } - - return rc; + efree(*input); } /* }}} */ |