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

Reply via email to