+++ /dev/null
-/*\r
- +----------------------------------------------------------------------+\r
- | PHP Version 5 |\r
- +----------------------------------------------------------------------+\r
- | Copyright (c) 1997-2011 The PHP Group |\r
- +----------------------------------------------------------------------+\r
- | This source file is subject to version 3.01 of the PHP license, |\r
- | that is bundled with this package in the file LICENSE, and is |\r
- | available through the world-wide-web at the following url: |\r
- | http://www.php.net/license/3_01.txt |\r
- | If you did not receive a copy of the PHP license and are unable to |\r
- | obtain it through the world-wide-web, please send a note to |\r
- | license@php.net so we can mail you a copy immediately. |\r
- +----------------------------------------------------------------------+\r
- | Author: Marcus Boerger <helly@php.net> |\r
- | Johannes Schlueter <johannes@php.net> |\r
- +----------------------------------------------------------------------+\r
-*/\r
-\r
-/* $Id: php_cli_readline.c 306939 2011-01-01 02:19:59Z felipe $ */\r
-\r
-#include "php.h"\r
-\r
-#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE)\r
-\r
-#ifndef HAVE_RL_COMPLETION_MATCHES\r
-#define rl_completion_matches completion_matches\r
-#endif\r
-\r
-#include "php_globals.h"\r
-#include "php_variables.h"\r
-#include "zend_hash.h"\r
-#include "zend_modules.h"\r
-\r
-#include "SAPI.h"\r
-\r
-#if HAVE_SETLOCALE\r
-#include <locale.h>\r
-#endif\r
-#include "zend.h"\r
-#include "zend_extensions.h"\r
-#include "php_ini.h"\r
-#include "php_globals.h"\r
-#include "php_main.h"\r
-#include "fopen_wrappers.h"\r
-#include "ext/standard/php_standard.h"\r
-\r
-#ifdef __riscos__\r
-#include <unixlib/local.h>\r
-#endif\r
-\r
-#if HAVE_LIBEDIT\r
-#include <editline/readline.h>\r
-#else\r
-#include <readline/readline.h>\r
-#include <readline/history.h>\r
-#endif\r
-\r
-#include "zend_compile.h"\r
-#include "zend_execute.h"\r
-#include "zend_highlight.h"\r
-#include "zend_indent.h"\r
-\r
-typedef enum {\r
- body,\r
- sstring,\r
- dstring,\r
- sstring_esc,\r
- dstring_esc,\r
- comment_line,\r
- comment_block,\r
- heredoc_start,\r
- heredoc,\r
- outside,\r
-} php_code_type;\r
-\r
-int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */\r
-{\r
- int valid_end = 1, last_valid_end;\r
- int brackets_count = 0;\r
- int brace_count = 0;\r
- int i;\r
- php_code_type code_type = body;\r
- char *heredoc_tag;\r
- int heredoc_len;\r
-\r
- for (i = 0; i < len; ++i) {\r
- switch(code_type) {\r
- default:\r
- switch(code[i]) {\r
- case '{':\r
- brackets_count++;\r
- valid_end = 0;\r
- break;\r
- case '}':\r
- if (brackets_count > 0) {\r
- brackets_count--;\r
- }\r
- valid_end = brackets_count ? 0 : 1;\r
- break;\r
- case '(':\r
- brace_count++;\r
- valid_end = 0;\r
- break;\r
- case ')':\r
- if (brace_count > 0) {\r
- brace_count--;\r
- }\r
- valid_end = 0;\r
- break;\r
- case ';':\r
- valid_end = brace_count == 0 && brackets_count == 0;\r
- break;\r
- case ' ':\r
- case '\r':\r
- case '\n':\r
- case '\t':\r
- break;\r
- case '\'':\r
- code_type = sstring;\r
- break;\r
- case '"':\r
- code_type = dstring;\r
- break;\r
- case '#':\r
- code_type = comment_line;\r
- break;\r
- case '/':\r
- if (code[i+1] == '/') {\r
- i++;\r
- code_type = comment_line;\r
- break;\r
- }\r
- if (code[i+1] == '*') {\r
- last_valid_end = valid_end;\r
- valid_end = 0;\r
- code_type = comment_block;\r
- i++;\r
- break;\r
- }\r
- valid_end = 0;\r
- break;\r
- case '%':\r
- if (!CG(asp_tags)) {\r
- valid_end = 0;\r
- break;\r
- }\r
- /* no break */\r
- case '?':\r
- if (code[i+1] == '>') {\r
- i++;\r
- code_type = outside;\r
- break;\r
- }\r
- valid_end = 0;\r
- break;\r
- case '<':\r
- valid_end = 0;\r
- if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {\r
- i += 2;\r
- code_type = heredoc_start;\r
- heredoc_len = 0;\r
- }\r
- break;\r
- default:\r
- valid_end = 0;\r
- break;\r
- }\r
- break;\r
- case sstring:\r
- if (code[i] == '\\') {\r
- code_type = sstring_esc;\r
- } else {\r
- if (code[i] == '\'') {\r
- code_type = body;\r
- }\r
- }\r
- break;\r
- case sstring_esc:\r
- code_type = sstring;\r
- break;\r
- case dstring:\r
- if (code[i] == '\\') {\r
- code_type = dstring_esc;\r
- } else {\r
- if (code[i] == '"') {\r
- code_type = body;\r
- }\r
- }\r
- break;\r
- case dstring_esc:\r
- code_type = dstring;\r
- break;\r
- case comment_line:\r
- if (code[i] == '\n') {\r
- code_type = body;\r
- }\r
- break;\r
- case comment_block:\r
- if (code[i-1] == '*' && code[i] == '/') {\r
- code_type = body;\r
- valid_end = last_valid_end;\r
- }\r
- break;\r
- case heredoc_start:\r
- switch(code[i]) {\r
- case ' ':\r
- case '\t':\r
- break;\r
- case '\r':\r
- case '\n':\r
- code_type = heredoc;\r
- break;\r
- default:\r
- if (!heredoc_len) {\r
- heredoc_tag = code+i;\r
- }\r
- heredoc_len++;\r
- break;\r
- }\r
- break;\r
- case heredoc:\r
- if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {\r
- code_type = body;\r
- } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') {\r
- code_type = body;\r
- valid_end = 1;\r
- }\r
- break;\r
- case outside:\r
- if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))\r
- || (CG(asp_tags) && !strncmp(code+i-1, "<%", 2))\r
- || (i > 3 && !strncmp(code+i-4, "<?php", 5))\r
- ) {\r
- code_type = body;\r
- }\r
- break;\r
- }\r
- }\r
-\r
- switch (code_type) {\r
- default:\r
- if (brace_count) {\r
- *prompt = "php ( ";\r
- } else if (brackets_count) {\r
- *prompt = "php { ";\r
- } else {\r
- *prompt = "php > ";\r
- }\r
- break;\r
- case sstring:\r
- case sstring_esc:\r
- *prompt = "php ' ";\r
- break;\r
- case dstring:\r
- case dstring_esc:\r
- *prompt = "php \" ";\r
- break;\r
- case comment_block:\r
- *prompt = "/* > ";\r
- break;\r
- case heredoc:\r
- *prompt = "<<< > ";\r
- break;\r
- case outside:\r
- *prompt = " > ";\r
- break;\r
- }\r
-\r
- if (!valid_end || brackets_count) {\r
- return 0;\r
- } else {\r
- return 1;\r
- }\r
-}\r
-/* }}} */\r
-\r
-static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */\r
-{\r
- char *name;\r
- ulong number;\r
-\r
- if (!(*state % 2)) {\r
- zend_hash_internal_pointer_reset(ht);\r
- (*state)++;\r
- }\r
- while(zend_hash_has_more_elements(ht) == SUCCESS) {\r
- zend_hash_get_current_key(ht, &name, &number, 0);\r
- if (!textlen || !strncmp(name, text, textlen)) {\r
- if (pData) {\r
- zend_hash_get_current_data(ht, pData);\r
- }\r
- zend_hash_move_forward(ht);\r
- return name;\r
- }\r
- if (zend_hash_move_forward(ht) == FAILURE) {\r
- break;\r
- }\r
- }\r
- (*state)++;\r
- return NULL;\r
-} /* }}} */\r
-\r
-static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */\r
-{\r
- char *retval, *tmp;\r
-\r
- tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC);\r
- if (retval) {\r
- retval = malloc(strlen(tmp) + 2);\r
- retval[0] = '$';\r
- strcpy(&retval[1], tmp);\r
- rl_completion_append_character = '\0';\r
- }\r
- return retval;\r
-} /* }}} */\r
-\r
-static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */\r
-{\r
- zend_function *func;\r
- char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);\r
- if (retval) {\r
- rl_completion_append_character = '(';\r
- retval = strdup(func->common.function_name);\r
- }\r
- \r
- return retval;\r
-} /* }}} */\r
-\r
-static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */\r
-{\r
- zend_class_entry **pce;\r
- char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);\r
- if (retval) {\r
- rl_completion_append_character = '\0';\r
- retval = strdup((*pce)->name);\r
- }\r
- \r
- return retval;\r
-} /* }}} */\r
-\r
-static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */\r
-{\r
- zend_class_entry **pce;\r
- char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);\r
- if (retval) {\r
- rl_completion_append_character = '\0';\r
- retval = strdup(retval);\r
- }\r
- \r
- return retval;\r
-} /* }}} */\r
-\r
-static int cli_completion_state;\r
-\r
-static char *cli_completion_generator(const char *text, int index) /* {{{ */\r
-{\r
-/*\r
-TODO:\r
-- constants\r
-- maybe array keys\r
-- language constructs and other things outside a hashtable (echo, try, function, class, ...)\r
-- object/class members\r
-\r
-- future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)\r
-*/\r
- char *retval = NULL;\r
- int textlen = strlen(text);\r
- TSRMLS_FETCH();\r
-\r
- if (!index) {\r
- cli_completion_state = 0;\r
- }\r
- if (text[0] == '$') {\r
- retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC);\r
- } else {\r
- char *lc_text, *class_name, *class_name_end;\r
- int class_name_len;\r
- zend_class_entry **pce = NULL;\r
- \r
- class_name_end = strstr(text, "::");\r
- if (class_name_end) {\r
- class_name_len = class_name_end - text;\r
- class_name = zend_str_tolower_dup(text, class_name_len);\r
- class_name[class_name_len] = '\0'; /* not done automatically */\r
- if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {\r
- efree(class_name);\r
- return NULL;\r
- }\r
- lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);\r
- textlen -= (class_name_len + 2);\r
- } else {\r
- lc_text = zend_str_tolower_dup(text, textlen);\r
- }\r
-\r
- switch (cli_completion_state) {\r
- case 0:\r
- case 1:\r
- retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);\r
- if (retval) {\r
- break;\r
- }\r
- case 2:\r
- case 3:\r
- retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);\r
- if (retval || pce) {\r
- break;\r
- }\r
- case 4:\r
- case 5:\r
- retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);\r
- break;\r
- default:\r
- break;\r
- }\r
- efree(lc_text);\r
- if (class_name_end) {\r
- efree(class_name);\r
- }\r
- if (pce && retval) {\r
- int len = class_name_len + 2 + strlen(retval) + 1;\r
- char *tmp = malloc(len);\r
- \r
- snprintf(tmp, len, "%s::%s", (*pce)->name, retval);\r
- free(retval);\r
- retval = tmp;\r
- }\r
- }\r
- \r
- return retval;\r
-} /* }}} */\r
-\r
-char **cli_code_completion(const char *text, int start, int end) /* {{{ */\r
-{\r
- return rl_completion_matches(text, cli_completion_generator);\r
-}\r
-/* }}} */\r
-\r
-#endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */\r
-\r
-/*\r
- * Local variables:\r
- * tab-width: 4\r
- * c-basic-offset: 4\r
- * End:\r
- * vim600: sw=4 ts=4 fdm=marker\r
- * vim<600: sw=4 ts=4\r
- */\r