johannes Thu, 20 May 2010 20:55:33 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=299537
Log: - Improved CLI Interactive readline shell (Johannes) . Added cli.pager ini setting to set a pager for output. . Added cli.prompt ini settingto configure the shell prompt. . Added shortcut #inisetting=value to change ini settings at run-time. . Don't terminate shell on fatal errors. A pager can be a an shell command which will receive the command output on its STDIN channel php > #cli.pager=less php > phpinfo(); (output will appear in the pager) php > #cli.pager=grep -i readline php > phpcredits(); Readline => Thies C. Arntzen php > #cli.pager= (output appears again direct on the terminal) A prompt can contain a few escape sequences like php > #cli.prompt=\e[032m\v \e[031m\b \e[34m\> \e[0m 5.3.99-dev php > //Colorful prompt with version number A prompt can also contaian PHP code in backticks php > #cli.prompt=`echo gethostname();` \b \> guybrush php > Changed paths: U php/php-src/trunk/NEWS U php/php-src/trunk/sapi/cli/php_cli.c U php/php-src/trunk/sapi/cli/php_cli_readline.c U php/php-src/trunk/sapi/cli/php_cli_readline.h
Modified: php/php-src/trunk/NEWS =================================================================== --- php/php-src/trunk/NEWS 2010-05-20 20:17:49 UTC (rev 299536) +++ php/php-src/trunk/NEWS 2010-05-20 20:55:33 UTC (rev 299537) @@ -47,6 +47,12 @@ - Changed session.entropy_file to default to /dev/urandom or /dev/arandom if either is present at compile time. (Rasmus) +- Improved CLI Interactive readline shell (Johannes) + . Added cli.pager ini setting to set a pager for output. + . Added cli.prompt ini settingto configure the shell prompt. + . Added shortcut #inisetting=value to change ini settings at run-time. + . Don't terminate shell on fatal errors. + - Removed legacy features: . allow_call_time_pass_reference. (Pierrick) . define_syslog_variables ini option and its associated function. (Kalle) Modified: php/php-src/trunk/sapi/cli/php_cli.c =================================================================== --- php/php-src/trunk/sapi/cli/php_cli.c 2010-05-20 20:17:49 UTC (rev 299536) +++ php/php-src/trunk/sapi/cli/php_cli.c 2010-05-20 20:55:33 UTC (rev 299537) @@ -132,6 +132,7 @@ static int php_optind = 1; #if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) static char php_last_char = '\0'; +static FILE *pager_pipe = NULL; #endif static const opt_struct OPTIONS[] = { @@ -258,7 +259,23 @@ { #ifdef PHP_WRITE_STDOUT long ret; +#endif +#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) + if (CLIR_G(prompt_str)) { + smart_str_appendl(CLIR_G(prompt_str), str, str_length); + return str_length; + } + + if (CLIR_G(pager) && *CLIR_G(pager) && !pager_pipe) { + pager_pipe = VCWD_POPEN(CLIR_G(pager), "w"); + } + if (pager_pipe) { + return fwrite(str, 1, MIN(str_length, 16384), pager_pipe); + } +#endif + +#ifdef PHP_WRITE_STDOUT do { ret = write(STDOUT_FILENO, str, str_length); } while (ret <= 0 && errno == EAGAIN && sapi_cli_select(STDOUT_FILENO TSRMLS_CC)); @@ -401,7 +418,11 @@ static int php_cli_startup(sapi_module_struct *sapi_module) /* {{{ */ { +#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE) + if (php_module_startup(sapi_module, &cli_readline_module_entry, 1)==FAILURE) { +#else if (php_module_startup(sapi_module, NULL, 0)==FAILURE) { +#endif return FAILURE; } return SUCCESS; @@ -1124,7 +1145,7 @@ char *line; size_t size = 4096, pos = 0, len; char *code = emalloc(size); - char *prompt = "php > "; + char *prompt = cli_get_prompt("php", '>' TSRMLS_CC); char *history_file; if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) { @@ -1158,6 +1179,27 @@ } len = strlen(line); + + if (line[0] == '#') { + char *param = strstr(&line[1], "="); + if (param) { + char *cmd; + uint cmd_len; + param++; + cmd_len = param - &line[1] - 1; + cmd = estrndup(&line[1], cmd_len); + + zend_alter_ini_entry_ex(cmd, cmd_len + 1, param, strlen(param), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); + efree(cmd); + add_history(line); + + efree(prompt); + /* TODO: This might be wrong! */ + prompt = cli_get_prompt("php", '>' TSRMLS_CC); + continue; + } + } + if (pos + len + 2 > size) { size = pos + len + 2; code = erealloc(code, size); @@ -1172,15 +1214,19 @@ } free(line); + efree(prompt); if (!cli_is_valid_code(code, pos, &prompt TSRMLS_CC)) { continue; } - zend_eval_stringl(code, pos, NULL, "php shell code" TSRMLS_CC); + zend_try { + zend_eval_stringl(code, pos, NULL, "php shell code" TSRMLS_CC); + } zend_end_try(); + pos = 0; - if (php_last_char != '\0' && php_last_char != '\n') { + if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') { sapi_cli_single_write("\n", 1 TSRMLS_CC); } @@ -1188,11 +1234,17 @@ zend_exception_error(EG(exception), E_WARNING TSRMLS_CC); } + if (pager_pipe) { + fclose(pager_pipe); + pager_pipe = NULL; + } + php_last_char = '\0'; } write_history(history_file); free(history_file); efree(code); + efree(prompt); exit_status = EG(exit_status); break; } Modified: php/php-src/trunk/sapi/cli/php_cli_readline.c =================================================================== --- php/php-src/trunk/sapi/cli/php_cli_readline.c 2010-05-20 20:17:49 UTC (rev 299536) +++ php/php-src/trunk/sapi/cli/php_cli_readline.c 2010-05-20 20:55:33 UTC (rev 299537) @@ -44,6 +44,7 @@ #include "php_main.h" #include "fopen_wrappers.h" #include "ext/standard/php_standard.h" +#include "ext/standard/php_smart_str.h" #ifdef __riscos__ #include <unixlib/local.h> @@ -61,6 +62,55 @@ #include "zend_highlight.h" #include "zend_indent.h" +#include "php_cli_readline.h" + +#define DEFAULT_PROMPT "\\b \\> " + +ZEND_DECLARE_MODULE_GLOBALS(cli_readline); + +static void cli_readline_init_globals(zend_cli_readline_globals *rg TSRMLS_DC) +{ + rg->pager = NULL; + rg->prompt = NULL; + rg->prompt_str = NULL; +} + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals) + STD_PHP_INI_ENTRY("cli.prompt", DEFAULT_PROMPT, PHP_INI_ALL, OnUpdateString, prompt, zend_cli_readline_globals, cli_readline_globals) +PHP_INI_END() + +static PHP_MINIT_FUNCTION(cli_readline) +{ + ZEND_INIT_MODULE_GLOBALS(cli_readline, cli_readline_init_globals, NULL); + REGISTER_INI_ENTRIES(); + return SUCCESS; +} + +static PHP_MSHUTDOWN_FUNCTION(cli_readline) +{ + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} + +static PHP_MINFO_FUNCTION(cli_readline) +{ + DISPLAY_INI_ENTRIES(); +} + +zend_module_entry cli_readline_module_entry = { + STANDARD_MODULE_HEADER, + "cli-readline", + NULL, + PHP_MINIT(cli_readline), + PHP_MSHUTDOWN(cli_readline), + NULL, + NULL, + PHP_MINFO(cli_readline), + PHP_VERSION, + STANDARD_MODULE_PROPERTIES +}; + typedef enum { body, sstring, @@ -74,6 +124,75 @@ outside, } php_code_type; +char *cli_get_prompt(char *block, char prompt TSRMLS_DC) /* {{{ */ +{ + smart_str retval = {0}; + char *prompt_spec = CLIR_G(prompt) ? CLIR_G(prompt) : DEFAULT_PROMPT; + + do { + if (*prompt_spec == '\\') { + switch (prompt_spec[1]) { + case '\\': + smart_str_appendc(&retval, '\\'); + prompt_spec++; + break; + case 'n': + smart_str_appendc(&retval, '\n'); + prompt_spec++; + break; + case 't': + smart_str_appendc(&retval, '\t'); + prompt_spec++; + break; + case 'e': + smart_str_appendc(&retval, '\033'); + prompt_spec++; + break; + + + case 'v': + smart_str_appends(&retval, PHP_VERSION); + prompt_spec++; + break; + case 'b': + smart_str_appends(&retval, block); + prompt_spec++; + break; + case '>': + smart_str_appendc(&retval, prompt); + prompt_spec++; + break; + case '`': + smart_str_appendc(&retval, '`'); + prompt_spec++; + break; + default: + smart_str_appendc(&retval, '\\'); + break; + } + } else if (*prompt_spec == '`') { + char *prompt_end = strstr(prompt_spec + 1, "`"); + char *code; + + if (prompt_end) { + code = estrndup(prompt_spec + 1, prompt_end - prompt_spec - 1); + + CLIR_G(prompt_str) = &retval; + zend_try { + zend_eval_stringl(code, prompt_end - prompt_spec - 1, NULL, "php prompt code" TSRMLS_CC); + } zend_end_try(); + CLIR_G(prompt_str) = NULL; + efree(code); + prompt_spec = prompt_end; + } + } else { + smart_str_appendc(&retval, *prompt_spec); + } + } while (++prompt_spec && *prompt_spec); + smart_str_0(&retval); + return retval.c; +} + int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */ { int valid_end = 1, last_valid_end; @@ -206,6 +325,7 @@ switch(code[i]) { case ' ': case '\t': + case '\'': break; case '\r': case '\n': @@ -241,29 +361,29 @@ switch (code_type) { default: if (brace_count) { - *prompt = "php ( "; + *prompt = cli_get_prompt("php", '(' TSRMLS_CC); } else if (brackets_count) { - *prompt = "php { "; + *prompt = cli_get_prompt("php", '{' TSRMLS_CC); } else { - *prompt = "php > "; + *prompt = cli_get_prompt("php", '>' TSRMLS_CC); } break; case sstring: case sstring_esc: - *prompt = "php ' "; + *prompt = cli_get_prompt("php", '\'' TSRMLS_CC); break; case dstring: case dstring_esc: - *prompt = "php \" "; + *prompt = cli_get_prompt("php", '"' TSRMLS_CC); break; case comment_block: - *prompt = "/* > "; + *prompt = cli_get_prompt("/* ", '>' TSRMLS_CC); break; case heredoc: - *prompt = "<<< > "; + *prompt = cli_get_prompt("<<<", '>' TSRMLS_CC); break; case outside: - *prompt = " > "; + *prompt = cli_get_prompt(" ", '>' TSRMLS_CC); break; } @@ -315,6 +435,20 @@ return retval; } /* }}} */ +static char *cli_completion_generator_ini(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */ +{ + char *retval, *tmp; + + tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(ini_directives), NULL TSRMLS_CC); + if (retval) { + retval = malloc(strlen(tmp) + 2); + retval[0] = '#'; + strcpy(&retval[1], tmp); + rl_completion_append_character = '='; + } + return retval; +} /* }}} */ + static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */ { zend_function *func; @@ -373,6 +507,8 @@ } if (text[0] == '$') { retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC); + } else if (text[0] == '#') { + retval = cli_completion_generator_ini(text, textlen, &cli_completion_state TSRMLS_CC); } else { char *lc_text, *class_name, *class_name_end; int class_name_len; Modified: php/php-src/trunk/sapi/cli/php_cli_readline.h =================================================================== --- php/php-src/trunk/sapi/cli/php_cli_readline.h 2010-05-20 20:17:49 UTC (rev 299536) +++ php/php-src/trunk/sapi/cli/php_cli_readline.h 2010-05-20 20:55:33 UTC (rev 299537) @@ -13,13 +13,32 @@ | lice...@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Marcus Boerger <he...@php.net> | + | Johannes Schlueter <johan...@php.net> | +----------------------------------------------------------------------+ */ /* $Id$ */ #include "php.h" +#include "ext/standard/php_smart_str.h" +ZEND_BEGIN_MODULE_GLOBALS(cli_readline) + char *pager; + char *prompt; + smart_str *prompt_str; +ZEND_END_MODULE_GLOBALS(cli_readline) + +#ifdef ZTS +# define CLIR_G(v) TSRMG(cli_readline_globals_id, zend_cli_readline_globals *, v) +#else +# define CLIR_G(v) (cli_readline_globals.v) +#endif + +ZEND_EXTERN_MODULE_GLOBALS(cli_readline) + +extern zend_module_entry cli_readline_module_entry; + +char *cli_get_prompt(char *block, char prompt TSRMLS_DC); int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC); char **cli_code_completion(const char *text, int start, int end);
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php