diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-03-14 05:42:27 +0000 |
---|---|---|
committer | <> | 2013-04-03 16:25:08 +0000 |
commit | c4dd7a1a684490673e25aaf4fabec5df138854c4 (patch) | |
tree | 4d57c44caae4480efff02b90b9be86f44bf25409 /ext/readline | |
download | php2-c4dd7a1a684490673e25aaf4fabec5df138854c4.tar.gz |
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/readline')
-rw-r--r-- | ext/readline/CREDITS | 2 | ||||
-rw-r--r-- | ext/readline/README.libedit | 4 | ||||
-rw-r--r-- | ext/readline/config.m4 | 124 | ||||
-rw-r--r-- | ext/readline/php_readline.h | 39 | ||||
-rw-r--r-- | ext/readline/readline.c | 650 | ||||
-rw-r--r-- | ext/readline/readline_cli.c | 767 | ||||
-rw-r--r-- | ext/readline/readline_cli.h | 43 | ||||
-rw-r--r-- | ext/readline/tests/readline_add_history_001.phpt | 32 | ||||
-rw-r--r-- | ext/readline/tests/readline_callback_handler_install_001.phpt | 24 | ||||
-rw-r--r-- | ext/readline/tests/readline_callback_handler_remove_001.phpt | 20 | ||||
-rw-r--r-- | ext/readline/tests/readline_clear_history_001.phpt | 16 | ||||
-rw-r--r-- | ext/readline/tests/readline_completion_function_001.phpt | 25 | ||||
-rw-r--r-- | ext/readline/tests/readline_info_001.phpt | 45 | ||||
-rw-r--r-- | ext/readline/tests/readline_list_history_001.phpt | 17 | ||||
-rw-r--r-- | ext/readline/tests/readline_read_history_001.phpt | 30 | ||||
-rw-r--r-- | ext/readline/tests/readline_write_history_001.phpt | 26 |
16 files changed, 1864 insertions, 0 deletions
diff --git a/ext/readline/CREDITS b/ext/readline/CREDITS new file mode 100644 index 0000000..02e44a4 --- /dev/null +++ b/ext/readline/CREDITS @@ -0,0 +1,2 @@ +Readline +Thies C. Arntzen diff --git a/ext/readline/README.libedit b/ext/readline/README.libedit new file mode 100644 index 0000000..a193710 --- /dev/null +++ b/ext/readline/README.libedit @@ -0,0 +1,4 @@ +This library can be built with libedit - non-GPL drop-in readline replacement. +Libedit can be obtained from http://sourceforge.net/projects/libedit/ +It is taken from NetBSD (http://www.netbsd.org/) CVS repository and modified +to work as stand-alone library. diff --git a/ext/readline/config.m4 b/ext/readline/config.m4 new file mode 100644 index 0000000..b1cb0b6 --- /dev/null +++ b/ext/readline/config.m4 @@ -0,0 +1,124 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_WITH(libedit,for libedit readline replacement, +[ --with-libedit[=DIR] Include libedit readline replacement (CLI/CGI only)]) + +if test "$PHP_LIBEDIT" = "no"; then + PHP_ARG_WITH(readline,for readline support, + [ --with-readline[=DIR] Include readline support (CLI/CGI only)]) +else + dnl "register" the --with-readline option to preven invalid "unknown configure option" warning + php_with_readline=no +fi + +if test "$PHP_READLINE" && test "$PHP_READLINE" != "no"; then + for i in $PHP_READLINE /usr/local /usr; do + test -f $i/include/readline/readline.h && READLINE_DIR=$i && break + done + + if test -z "$READLINE_DIR"; then + AC_MSG_ERROR(Please reinstall readline - I cannot find readline.h) + fi + + PHP_ADD_INCLUDE($READLINE_DIR/include) + + PHP_READLINE_LIBS="" + AC_CHECK_LIB(ncurses, tgetent, + [ + PHP_ADD_LIBRARY(ncurses,,READLINE_SHARED_LIBADD) + PHP_READLINE_LIBS="$PHP_READLINE_LIBS -lncurses" + ],[ + AC_CHECK_LIB(termcap, tgetent, + [ + PHP_ADD_LIBRARY(termcap,,READLINE_SHARED_LIBADD) + PHP_READLINE_LIBS="$PHP_READLINE_LIBS -ltermcap" + ]) + ]) + + PHP_CHECK_LIBRARY(readline, readline, + [ + PHP_ADD_LIBRARY_WITH_PATH(readline, $READLINE_DIR/$PHP_LIBDIR, READLINE_SHARED_LIBADD) + ], [ + AC_MSG_ERROR(readline library not found) + ], [ + -L$READLINE_DIR/$PHP_LIBDIR $PHP_READLINE_LIBS + ]) + + PHP_CHECK_LIBRARY(readline, rl_pending_input, + [], [ + AC_MSG_ERROR([invalid readline installation detected. Try --with-libedit instead.]) + ], [ + -L$READLINE_DIR/$PHP_LIBDIR $PHP_READLINE_LIBS + ]) + + PHP_CHECK_LIBRARY(readline, rl_callback_read_char, + [ + AC_DEFINE(HAVE_RL_CALLBACK_READ_CHAR, 1, [ ]) + ],[],[ + -L$READLINE_DIR/$PHP_LIBDIR $PHP_READLINE_LIBS + ]) + + PHP_CHECK_LIBRARY(edit, rl_on_new_line, + [ + AC_DEFINE(HAVE_RL_ON_NEW_LINE, 1, [ ]) + ],[],[ + -L$READLINE_DIR/$PHP_LIBDIR $PHP_READLINE_LIBS + ]) + + AC_DEFINE(HAVE_LIBREADLINE, 1, [ ]) + +elif test "$PHP_LIBEDIT" != "no"; then + + for i in $PHP_LIBEDIT /usr/local /usr; do + test -f $i/include/editline/readline.h && LIBEDIT_DIR=$i && break + done + + if test -z "$LIBEDIT_DIR"; then + AC_MSG_ERROR(Please reinstall libedit - I cannot find readline.h) + fi + + PHP_ADD_INCLUDE($LIBEDIT_DIR/include) + + AC_CHECK_LIB(ncurses, tgetent, + [ + PHP_ADD_LIBRARY(ncurses,,READLINE_SHARED_LIBADD) + ],[ + AC_CHECK_LIB(termcap, tgetent, + [ + PHP_ADD_LIBRARY(termcap,,READLINE_SHARED_LIBADD) + ]) + ]) + + PHP_CHECK_LIBRARY(edit, readline, + [ + PHP_ADD_LIBRARY_WITH_PATH(edit, $LIBEDIT_DIR/$PHP_LIBDIR, READLINE_SHARED_LIBADD) + ], [ + AC_MSG_ERROR(edit library required by readline not found) + ], [ + -L$READLINE_DIR/$PHP_LIBDIR + ]) + + PHP_CHECK_LIBRARY(edit, rl_callback_read_char, + [ + AC_DEFINE(HAVE_RL_CALLBACK_READ_CHAR, 1, [ ]) + ],[],[ + -L$READLINE_DIR/$PHP_LIBDIR + ]) + + PHP_CHECK_LIBRARY(edit, rl_on_new_line, + [ + AC_DEFINE(HAVE_RL_ON_NEW_LINE, 1, [ ]) + ],[],[ + -L$READLINE_DIR/$PHP_LIBDIR + ]) + + AC_DEFINE(HAVE_LIBEDIT, 1, [ ]) +fi + +if test "$PHP_READLINE" != "no" || test "$PHP_LIBEDIT" != "no"; then + AC_CHECK_FUNCS([rl_completion_matches]) + PHP_NEW_EXTENSION(readline, readline.c readline_cli.c, $ext_shared, cli) + PHP_SUBST(READLINE_SHARED_LIBADD) +fi diff --git a/ext/readline/php_readline.h b/ext/readline/php_readline.h new file mode 100644 index 0000000..a9f8287 --- /dev/null +++ b/ext/readline/php_readline.h @@ -0,0 +1,39 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Thies C. Arntzen <thies@thieso.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_READLINE_H +#define PHP_READLINE_H + +#if HAVE_LIBREADLINE || HAVE_LIBEDIT +#ifdef ZTS +#warning Readline module will *NEVER* be thread-safe +#endif + +extern zend_module_entry readline_module_entry; +#define phpext_readline_ptr &readline_module_entry + +#else + +#define phpext_readline_ptr NULL + +#endif /* HAVE_LIBREADLINE */ + +#endif /* PHP_READLINE_H */ + diff --git a/ext/readline/readline.c b/ext/readline/readline.c new file mode 100644 index 0000000..3148f80 --- /dev/null +++ b/ext/readline/readline.c @@ -0,0 +1,650 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Thies C. Arntzen <thies@thieso.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* {{{ includes & prototypes */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_readline.h" +#include "readline_cli.h" + +#if HAVE_LIBREADLINE || HAVE_LIBEDIT + +#ifndef HAVE_RL_COMPLETION_MATCHES +#define rl_completion_matches completion_matches +#endif + +#ifdef HAVE_LIBEDIT +#include <editline/readline.h> +#else +#include <readline/readline.h> +#include <readline/history.h> +#endif + +PHP_FUNCTION(readline); +PHP_FUNCTION(readline_add_history); +PHP_FUNCTION(readline_info); +PHP_FUNCTION(readline_clear_history); +#ifndef HAVE_LIBEDIT +PHP_FUNCTION(readline_list_history); +#endif +PHP_FUNCTION(readline_read_history); +PHP_FUNCTION(readline_write_history); +PHP_FUNCTION(readline_completion_function); + +#if HAVE_RL_CALLBACK_READ_CHAR +PHP_FUNCTION(readline_callback_handler_install); +PHP_FUNCTION(readline_callback_read_char); +PHP_FUNCTION(readline_callback_handler_remove); +PHP_FUNCTION(readline_redisplay); +PHP_FUNCTION(readline_on_new_line); + +static zval *_prepped_callback = NULL; + +#endif + +static zval *_readline_completion = NULL; +static zval _readline_array; + +PHP_MINIT_FUNCTION(readline); +PHP_MSHUTDOWN_FUNCTION(readline); +PHP_RSHUTDOWN_FUNCTION(readline); +PHP_MINFO_FUNCTION(readline); + +/* }}} */ + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline, 0, 0, 0) + ZEND_ARG_INFO(0, prompt) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_info, 0, 0, 0) + ZEND_ARG_INFO(0, varname) + ZEND_ARG_INFO(0, newvalue) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_add_history, 0, 0, 1) + ZEND_ARG_INFO(0, prompt) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_readline_clear_history, 0) +ZEND_END_ARG_INFO() + +#ifndef HAVE_LIBEDIT +ZEND_BEGIN_ARG_INFO(arginfo_readline_list_history, 0) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_read_history, 0, 0, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_write_history, 0, 0, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_completion_function, 0, 0, 1) + ZEND_ARG_INFO(0, funcname) +ZEND_END_ARG_INFO() + +#if HAVE_RL_CALLBACK_READ_CHAR +ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_callback_handler_install, 0, 0, 2) + ZEND_ARG_INFO(0, prompt) + ZEND_ARG_INFO(0, callback) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_read_char, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_handler_remove, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0) +ZEND_END_ARG_INFO() +#endif +/* }}} */ + +/* {{{ module stuff */ +static const zend_function_entry php_readline_functions[] = { + PHP_FE(readline, arginfo_readline) + PHP_FE(readline_info, arginfo_readline_info) + PHP_FE(readline_add_history, arginfo_readline_add_history) + PHP_FE(readline_clear_history, arginfo_readline_clear_history) +#ifndef HAVE_LIBEDIT + PHP_FE(readline_list_history, arginfo_readline_list_history) +#endif + PHP_FE(readline_read_history, arginfo_readline_read_history) + PHP_FE(readline_write_history, arginfo_readline_write_history) + PHP_FE(readline_completion_function,arginfo_readline_completion_function) +#if HAVE_RL_CALLBACK_READ_CHAR + PHP_FE(readline_callback_handler_install, arginfo_readline_callback_handler_install) + PHP_FE(readline_callback_read_char, arginfo_readline_callback_read_char) + PHP_FE(readline_callback_handler_remove, arginfo_readline_callback_handler_remove) + PHP_FE(readline_redisplay, arginfo_readline_redisplay) +#endif +#if HAVE_RL_ON_NEW_LINE + PHP_FE(readline_on_new_line, arginfo_readline_on_new_line) +#endif + PHP_FE_END +}; + +zend_module_entry readline_module_entry = { + STANDARD_MODULE_HEADER, + "readline", + php_readline_functions, + PHP_MINIT(readline), + PHP_MSHUTDOWN(readline), + NULL, + PHP_RSHUTDOWN(readline), + PHP_MINFO(readline), + PHP_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_READLINE +ZEND_GET_MODULE(readline) +#endif + +PHP_MINIT_FUNCTION(readline) +{ + using_history(); + return PHP_MINIT(cli_readline)(INIT_FUNC_ARGS_PASSTHRU); +} + +PHP_MSHUTDOWN_FUNCTION(readline) +{ + return PHP_MSHUTDOWN(cli_readline)(SHUTDOWN_FUNC_ARGS_PASSTHRU); +} + +PHP_RSHUTDOWN_FUNCTION(readline) +{ + if (_readline_completion) { + zval_dtor(_readline_completion); + FREE_ZVAL(_readline_completion); + } +#if HAVE_RL_CALLBACK_READ_CHAR + if (_prepped_callback) { + rl_callback_handler_remove(); + zval_ptr_dtor(&_prepped_callback); + _prepped_callback = 0; + } +#endif + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(readline) +{ + PHP_MINFO(cli_readline)(ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU); +} + +/* }}} */ + +/* {{{ proto string readline([string prompt]) + Reads a line */ +PHP_FUNCTION(readline) +{ + char *prompt = NULL; + int prompt_len; + char *result; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &prompt, &prompt_len)) { + RETURN_FALSE; + } + + result = readline(prompt); + + if (! result) { + RETURN_FALSE; + } else { + RETVAL_STRING(result,1); + free(result); + } +} + +/* }}} */ + +#define SAFE_STRING(s) ((s)?(char*)(s):"") + +/* {{{ proto mixed readline_info([string varname [, string newvalue]]) + Gets/sets various internal readline variables. */ +PHP_FUNCTION(readline_info) +{ + char *what = NULL; + zval **value = NULL; + int what_len, oldval; + char *oldstr; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sZ", &what, &what_len, &value) == FAILURE) { + return; + } + + if (!what) { + array_init(return_value); + add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer),1); + add_assoc_long(return_value,"point",rl_point); + add_assoc_long(return_value,"end",rl_end); +#ifdef HAVE_LIBREADLINE + add_assoc_long(return_value,"mark",rl_mark); + add_assoc_long(return_value,"done",rl_done); + add_assoc_long(return_value,"pending_input",rl_pending_input); + add_assoc_string(return_value,"prompt",SAFE_STRING(rl_prompt),1); + add_assoc_string(return_value,"terminal_name",(char *)SAFE_STRING(rl_terminal_name),1); +#endif +#if HAVE_ERASE_EMPTY_LINE + add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line); +#endif + add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version),1); + add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name),1); + } else { + if (!strcasecmp(what,"line_buffer")) { + oldstr = rl_line_buffer; + if (value) { + /* XXX if (rl_line_buffer) free(rl_line_buffer); */ + convert_to_string_ex(value); + rl_line_buffer = strdup(Z_STRVAL_PP(value)); + } + RETVAL_STRING(SAFE_STRING(oldstr),1); + } else if (!strcasecmp(what, "point")) { + RETVAL_LONG(rl_point); + } else if (!strcasecmp(what, "end")) { + RETVAL_LONG(rl_end); +#ifdef HAVE_LIBREADLINE + } else if (!strcasecmp(what, "mark")) { + RETVAL_LONG(rl_mark); + } else if (!strcasecmp(what, "done")) { + oldval = rl_done; + if (value) { + convert_to_long_ex(value); + rl_done = Z_LVAL_PP(value); + } + RETVAL_LONG(oldval); + } else if (!strcasecmp(what, "pending_input")) { + oldval = rl_pending_input; + if (value) { + convert_to_string_ex(value); + rl_pending_input = Z_STRVAL_PP(value)[0]; + } + RETVAL_LONG(oldval); + } else if (!strcasecmp(what, "prompt")) { + RETVAL_STRING(SAFE_STRING(rl_prompt),1); + } else if (!strcasecmp(what, "terminal_name")) { + RETVAL_STRING((char *)SAFE_STRING(rl_terminal_name),1); +#endif +#if HAVE_ERASE_EMPTY_LINE + } else if (!strcasecmp(what, "erase_empty_line")) { + oldval = rl_erase_empty_line; + if (value) { + convert_to_long_ex(value); + rl_erase_empty_line = Z_LVAL_PP(value); + } + RETVAL_LONG(oldval); +#endif + } else if (!strcasecmp(what,"library_version")) { + RETVAL_STRING((char *)SAFE_STRING(rl_library_version),1); + } else if (!strcasecmp(what, "readline_name")) { + oldstr = (char*)rl_readline_name; + if (value) { + /* XXX if (rl_readline_name) free(rl_readline_name); */ + convert_to_string_ex(value); + rl_readline_name = strdup(Z_STRVAL_PP(value));; + } + RETVAL_STRING(SAFE_STRING(oldstr),1); + } + } +} + +/* }}} */ +/* {{{ proto bool readline_add_history(string prompt) + Adds a line to the history */ +PHP_FUNCTION(readline_add_history) +{ + char *arg; + int arg_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { + return; + } + + add_history(arg); + + RETURN_TRUE; +} + +/* }}} */ +/* {{{ proto bool readline_clear_history(void) + Clears the history */ +PHP_FUNCTION(readline_clear_history) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + clear_history(); + + RETURN_TRUE; +} + +/* }}} */ +/* {{{ proto array readline_list_history(void) + Lists the history */ +#ifndef HAVE_LIBEDIT +PHP_FUNCTION(readline_list_history) +{ + HIST_ENTRY **history; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + history = history_list(); + + array_init(return_value); + + if (history) { + int i; + for (i = 0; history[i]; i++) { + add_next_index_string(return_value,history[i]->line,1); + } + } +} +#endif +/* }}} */ +/* {{{ proto bool readline_read_history([string filename]) + Reads the history */ +PHP_FUNCTION(readline_read_history) +{ + char *arg = NULL; + int arg_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|p", &arg, &arg_len) == FAILURE) { + return; + } + + if (php_check_open_basedir(arg TSRMLS_CC)) { + RETURN_FALSE; + } + + /* XXX from & to NYI */ + if (read_history(arg)) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} + +/* }}} */ +/* {{{ proto bool readline_write_history([string filename]) + Writes the history */ +PHP_FUNCTION(readline_write_history) +{ + char *arg = NULL; + int arg_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|p", &arg, &arg_len) == FAILURE) { + return; + } + + if (php_check_open_basedir(arg TSRMLS_CC)) { + RETURN_FALSE; + } + + if (write_history(arg)) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} + +/* }}} */ +/* {{{ proto bool readline_completion_function(string funcname) + Readline completion function? */ + +static char *_readline_command_generator(const char *text, int state) +{ + HashTable *myht = Z_ARRVAL(_readline_array); + zval **entry; + + if (!state) { + zend_hash_internal_pointer_reset(myht); + } + + while (zend_hash_get_current_data(myht, (void **)&entry) == SUCCESS) { + zend_hash_move_forward(myht); + + convert_to_string_ex(entry); + if (strncmp (Z_STRVAL_PP(entry), text, strlen(text)) == 0) { + return (strdup(Z_STRVAL_PP(entry))); + } + } + + return NULL; +} + +static zval *_readline_string_zval(const char *str) +{ + zval *ret; + int len; + + MAKE_STD_ZVAL(ret); + + if (str) { + len = strlen(str); + ZVAL_STRINGL(ret, (char*)str, len, 1); + } else { + ZVAL_NULL(ret); + } + + return ret; +} + +static zval *_readline_long_zval(long l) +{ + zval *ret; + MAKE_STD_ZVAL(ret); + + Z_TYPE_P(ret) = IS_LONG; + Z_LVAL_P(ret) = l; + return ret; +} + +static char **_readline_completion_cb(const char *text, int start, int end) +{ + zval *params[3]; + int i; + char **matches = NULL; + TSRMLS_FETCH(); + + params[0]=_readline_string_zval(text); + params[1]=_readline_long_zval(start); + params[2]=_readline_long_zval(end); + + if (call_user_function(CG(function_table), NULL, _readline_completion, &_readline_array, 3, params TSRMLS_CC) == SUCCESS) { + if (Z_TYPE(_readline_array) == IS_ARRAY) { + if (zend_hash_num_elements(Z_ARRVAL(_readline_array))) { + matches = rl_completion_matches(text,_readline_command_generator); + } else { + matches = malloc(sizeof(char *) * 2); + if (!matches) { + return NULL; + } + matches[0] = strdup(""); + matches[1] = '\0'; + } + } + } + + for (i = 0; i < 3; i++) { + zval_ptr_dtor(¶ms[i]); + } + zval_dtor(&_readline_array); + + return matches; +} + +PHP_FUNCTION(readline_completion_function) +{ + zval *arg = NULL; + char *name = NULL; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg)) { + RETURN_FALSE; + } + + if (!zend_is_callable(arg, 0, &name TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name); + efree(name); + RETURN_FALSE; + } + efree(name); + + if (_readline_completion) { + zval_dtor(_readline_completion); + FREE_ZVAL(_readline_completion); + } + + MAKE_STD_ZVAL(_readline_completion); + *_readline_completion = *arg; + zval_copy_ctor(_readline_completion); + + rl_attempted_completion_function = _readline_completion_cb; + if (rl_attempted_completion_function == NULL) { + efree(name); + RETURN_FALSE; + } + RETURN_TRUE; +} + +/* }}} */ + +#if HAVE_RL_CALLBACK_READ_CHAR + +static void php_rl_callback_handler(char *the_line) +{ + zval *params[1]; + zval dummy; + TSRMLS_FETCH(); + + ZVAL_NULL(&dummy); + + params[0] = _readline_string_zval(the_line); + + call_user_function(CG(function_table), NULL, _prepped_callback, &dummy, 1, params TSRMLS_CC); + + zval_ptr_dtor(¶ms[0]); + zval_dtor(&dummy); +} + +/* {{{ proto void readline_callback_handler_install(string prompt, mixed callback) + Initializes the readline callback interface and terminal, prints the prompt and returns immediately */ +PHP_FUNCTION(readline_callback_handler_install) +{ + zval *callback; + char *name = NULL; + char *prompt; + int prompt_len; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &prompt, &prompt_len, &callback)) { + return; + } + + if (!zend_is_callable(callback, 0, &name TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name); + efree(name); + RETURN_FALSE; + } + efree(name); + + if (_prepped_callback) { + rl_callback_handler_remove(); + zval_dtor(_prepped_callback); + FREE_ZVAL(_prepped_callback); + } + + ALLOC_ZVAL(_prepped_callback); + MAKE_COPY_ZVAL(&callback, _prepped_callback); + + rl_callback_handler_install(prompt, php_rl_callback_handler); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto void readline_callback_read_char() + Informs the readline callback interface that a character is ready for input */ +PHP_FUNCTION(readline_callback_read_char) +{ + if (_prepped_callback) { + rl_callback_read_char(); + } +} +/* }}} */ + +/* {{{ proto bool readline_callback_handler_remove() + Removes a previously installed callback handler and restores terminal settings */ +PHP_FUNCTION(readline_callback_handler_remove) +{ + if (_prepped_callback) { + rl_callback_handler_remove(); + zval_dtor(_prepped_callback); + FREE_ZVAL(_prepped_callback); + _prepped_callback = 0; + RETURN_TRUE; + } + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto void readline_redisplay(void) + Ask readline to redraw the display */ +PHP_FUNCTION(readline_redisplay) +{ + rl_redisplay(); +} +/* }}} */ + +#endif + +#if HAVE_RL_ON_NEW_LINE +/* {{{ proto void readline_on_new_line(void) + Inform readline that the cursor has moved to a new line */ +PHP_FUNCTION(readline_on_new_line) +{ + rl_on_new_line(); +} +/* }}} */ + +#endif + + +#endif /* HAVE_LIBREADLINE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/readline/readline_cli.c b/ext/readline/readline_cli.c new file mode 100644 index 0000000..f4ce12b --- /dev/null +++ b/ext/readline/readline_cli.c @@ -0,0 +1,767 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Marcus Boerger <helly@php.net> | + | Johannes Schlueter <johannes@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" + +#ifndef HAVE_RL_COMPLETION_MATCHES +#define rl_completion_matches completion_matches +#endif + +#include "php_globals.h" +#include "php_variables.h" +#include "zend_hash.h" +#include "zend_modules.h" + +#include "SAPI.h" + +#if HAVE_SETLOCALE +#include <locale.h> +#endif +#include "zend.h" +#include "zend_extensions.h" +#include "php_ini.h" +#include "php_globals.h" +#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> +#endif + +#if HAVE_LIBEDIT +#include <editline/readline.h> +#else +#include <readline/readline.h> +#include <readline/history.h> +#endif + +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_highlight.h" +#include "zend_indent.h" +#include "zend_exceptions.h" + +#include "sapi/cli/cli.h" +#include "readline_cli.h" + +#ifdef COMPILE_DL_READLINE +#include <dlfcn.h> +#endif + +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT NULL +#endif + +#define DEFAULT_PROMPT "\\b \\> " + +ZEND_DECLARE_MODULE_GLOBALS(cli_readline); + +static char php_last_char = '\0'; +static FILE *pager_pipe = NULL; + +static size_t readline_shell_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */ +{ + 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); + } + + return -1; +} +/* }}} */ + +static int readline_shell_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */ +{ + php_last_char = str[str_length-1]; + return -1; +} +/* }}} */ + +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() + + + +typedef enum { + body, + sstring, + dstring, + sstring_esc, + dstring_esc, + comment_line, + comment_block, + heredoc_start, + heredoc, + outside, +} php_code_type; + +static 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; +} +/* }}} */ + +static int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */ +{ + int valid_end = 1, last_valid_end; + int brackets_count = 0; + int brace_count = 0; + int i; + php_code_type code_type = body; + char *heredoc_tag; + int heredoc_len; + + for (i = 0; i < len; ++i) { + switch(code_type) { + default: + switch(code[i]) { + case '{': + brackets_count++; + valid_end = 0; + break; + case '}': + if (brackets_count > 0) { + brackets_count--; + } + valid_end = brackets_count ? 0 : 1; + break; + case '(': + brace_count++; + valid_end = 0; + break; + case ')': + if (brace_count > 0) { + brace_count--; + } + valid_end = 0; + break; + case ';': + valid_end = brace_count == 0 && brackets_count == 0; + break; + case ' ': + case '\r': + case '\n': + case '\t': + break; + case '\'': + code_type = sstring; + break; + case '"': + code_type = dstring; + break; + case '#': + code_type = comment_line; + break; + case '/': + if (code[i+1] == '/') { + i++; + code_type = comment_line; + break; + } + if (code[i+1] == '*') { + last_valid_end = valid_end; + valid_end = 0; + code_type = comment_block; + i++; + break; + } + valid_end = 0; + break; + case '%': + if (!CG(asp_tags)) { + valid_end = 0; + break; + } + /* no break */ + case '?': + if (code[i+1] == '>') { + i++; + code_type = outside; + break; + } + valid_end = 0; + break; + case '<': + valid_end = 0; + if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') { + i += 2; + code_type = heredoc_start; + heredoc_len = 0; + } + break; + default: + valid_end = 0; + break; + } + break; + case sstring: + if (code[i] == '\\') { + code_type = sstring_esc; + } else { + if (code[i] == '\'') { + code_type = body; + } + } + break; + case sstring_esc: + code_type = sstring; + break; + case dstring: + if (code[i] == '\\') { + code_type = dstring_esc; + } else { + if (code[i] == '"') { + code_type = body; + } + } + break; + case dstring_esc: + code_type = dstring; + break; + case comment_line: + if (code[i] == '\n') { + code_type = body; + } + break; + case comment_block: + if (code[i-1] == '*' && code[i] == '/') { + code_type = body; + valid_end = last_valid_end; + } + break; + case heredoc_start: + switch(code[i]) { + case ' ': + case '\t': + case '\'': + break; + case '\r': + case '\n': + code_type = heredoc; + break; + default: + if (!heredoc_len) { + heredoc_tag = code+i; + } + heredoc_len++; + break; + } + break; + case heredoc: + if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') { + code_type = body; + } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') { + code_type = body; + valid_end = 1; + } + break; + case outside: + if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2)) + || (CG(asp_tags) && !strncmp(code+i-1, "<%", 2)) + || (i > 3 && !strncmp(code+i-4, "<?php", 5)) + ) { + code_type = body; + } + break; + } + } + + switch (code_type) { + default: + if (brace_count) { + *prompt = cli_get_prompt("php", '(' TSRMLS_CC); + } else if (brackets_count) { + *prompt = cli_get_prompt("php", '{' TSRMLS_CC); + } else { + *prompt = cli_get_prompt("php", '>' TSRMLS_CC); + } + break; + case sstring: + case sstring_esc: + *prompt = cli_get_prompt("php", '\'' TSRMLS_CC); + break; + case dstring: + case dstring_esc: + *prompt = cli_get_prompt("php", '"' TSRMLS_CC); + break; + case comment_block: + *prompt = cli_get_prompt("/* ", '>' TSRMLS_CC); + break; + case heredoc: + *prompt = cli_get_prompt("<<<", '>' TSRMLS_CC); + break; + case outside: + *prompt = cli_get_prompt(" ", '>' TSRMLS_CC); + break; + } + + if (!valid_end || brackets_count) { + return 0; + } else { + return 1; + } +} +/* }}} */ + +static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */ +{ + char *name; + ulong number; + + if (!(*state % 2)) { + zend_hash_internal_pointer_reset(ht); + (*state)++; + } + while(zend_hash_has_more_elements(ht) == SUCCESS) { + zend_hash_get_current_key(ht, &name, &number, 0); + if (!textlen || !strncmp(name, text, textlen)) { + if (pData) { + zend_hash_get_current_data(ht, pData); + } + zend_hash_move_forward(ht); + return name; + } + if (zend_hash_move_forward(ht) == FAILURE) { + break; + } + } + (*state)++; + return NULL; +} /* }}} */ + +static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */ +{ + char *retval, *tmp; + + tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC); + if (retval) { + retval = malloc(strlen(tmp) + 2); + retval[0] = '$'; + strcpy(&retval[1], tmp); + rl_completion_append_character = '\0'; + } + 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; + char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC); + if (retval) { + rl_completion_append_character = '('; + retval = strdup(func->common.function_name); + } + + return retval; +} /* }}} */ + +static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */ +{ + zend_class_entry **pce; + char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC); + if (retval) { + rl_completion_append_character = '\0'; + retval = strdup((*pce)->name); + } + + return retval; +} /* }}} */ + +static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */ +{ + zend_class_entry **pce; + char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC); + if (retval) { + rl_completion_append_character = '\0'; + retval = strdup(retval); + } + + return retval; +} /* }}} */ + +static int cli_completion_state; + +static char *cli_completion_generator(const char *text, int index) /* {{{ */ +{ +/* +TODO: +- constants +- maybe array keys +- language constructs and other things outside a hashtable (echo, try, function, class, ...) +- object/class members + +- future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...) +*/ + char *retval = NULL; + int textlen = strlen(text); + TSRMLS_FETCH(); + + if (!index) { + cli_completion_state = 0; + } + 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; + zend_class_entry **pce = NULL; + + class_name_end = strstr(text, "::"); + if (class_name_end) { + class_name_len = class_name_end - text; + class_name = zend_str_tolower_dup(text, class_name_len); + class_name[class_name_len] = '\0'; /* not done automatically */ + if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) { + efree(class_name); + return NULL; + } + lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len); + textlen -= (class_name_len + 2); + } else { + lc_text = zend_str_tolower_dup(text, textlen); + } + + switch (cli_completion_state) { + case 0: + case 1: + retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC); + if (retval) { + break; + } + case 2: + case 3: + retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC); + if (retval || pce) { + break; + } + case 4: + case 5: + retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC); + break; + default: + break; + } + efree(lc_text); + if (class_name_end) { + efree(class_name); + } + if (pce && retval) { + int len = class_name_len + 2 + strlen(retval) + 1; + char *tmp = malloc(len); + + snprintf(tmp, len, "%s::%s", (*pce)->name, retval); + free(retval); + retval = tmp; + } + } + + return retval; +} /* }}} */ + +static char **cli_code_completion(const char *text, int start, int end) /* {{{ */ +{ + return rl_completion_matches(text, cli_completion_generator); +} +/* }}} */ + +static int readline_shell_run(TSRMLS_D) /* {{{ */ +{ + char *line; + size_t size = 4096, pos = 0, len; + char *code = emalloc(size); + char *prompt = cli_get_prompt("php", '>' TSRMLS_CC); + char *history_file; + + if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) { + zend_file_handle *prepend_file_p; + zend_file_handle prepend_file = {0}; + + prepend_file.filename = PG(auto_prepend_file); + prepend_file.opened_path = NULL; + prepend_file.free_filename = 0; + prepend_file.type = ZEND_HANDLE_FILENAME; + prepend_file_p = &prepend_file; + + zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 1, prepend_file_p); + } + + history_file = tilde_expand("~/.php_history"); + rl_attempted_completion_function = cli_code_completion; + rl_special_prefixes = "$"; + read_history(history_file); + + EG(exit_status) = 0; + while ((line = readline(prompt)) != NULL) { + if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) { + free(line); + break; + } + + if (!pos && !*line) { + free(line); + continue; + } + + 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); + } + memcpy(&code[pos], line, len); + pos += len; + code[pos] = '\n'; + code[++pos] = '\0'; + + if (*line) { + add_history(line); + } + + free(line); + efree(prompt); + + if (!cli_is_valid_code(code, pos, &prompt TSRMLS_CC)) { + continue; + } + + zend_try { + zend_eval_stringl(code, pos, NULL, "php shell code" TSRMLS_CC); + } zend_end_try(); + + pos = 0; + + if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') { + readline_shell_write("\n", 1 TSRMLS_CC); + } + + if (EG(exception)) { + 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); + return EG(exit_status); +} +/* }}} */ + +/* +#ifdef COMPILE_DL_READLINE +This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only +extensions. If that is being changed dlsym() should only be used when building +this extension sharedto offer compatibility. +*/ +#define GET_SHELL_CB(cb) \ + do { \ + (cb) = NULL; \ + cli_shell_callbacks_t *(*get_callbacks)(); \ + get_callbacks = dlsym(RTLD_DEFAULT, "php_cli_get_shell_callbacks"); \ + if (get_callbacks) { \ + (cb) = get_callbacks(); \ + } \ + } while(0) +/*#else +#define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks() +#endif*/ + +PHP_MINIT_FUNCTION(cli_readline) +{ + cli_shell_callbacks_t *cb; + + ZEND_INIT_MODULE_GLOBALS(cli_readline, cli_readline_init_globals, NULL); + REGISTER_INI_ENTRIES(); + +#if HAVE_LIBEDIT + REGISTER_STRING_CONSTANT("READLINE_LIB", "libedit", CONST_CS|CONST_PERSISTENT); +#else + REGISTER_STRING_CONSTANT("READLINE_LIB", "readline", CONST_CS|CONST_PERSISTENT); +#endif + + GET_SHELL_CB(cb); + if (cb) { + cb->cli_shell_write = readline_shell_write; + cb->cli_shell_ub_write = readline_shell_ub_write; + cb->cli_shell_run = readline_shell_run; + } + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(cli_readline) +{ + cli_shell_callbacks_t *cb; + + UNREGISTER_INI_ENTRIES(); + + GET_SHELL_CB(cb); + if (cb) { + cb->cli_shell_write = NULL; + cb->cli_shell_ub_write = NULL; + cb->cli_shell_run = NULL; + } + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(cli_readline) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "Readline Support", "enabled"); + php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown")); + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/readline/readline_cli.h b/ext/readline/readline_cli.h new file mode 100644 index 0000000..d9deb8a --- /dev/null +++ b/ext/readline/readline_cli.h @@ -0,0 +1,43 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + | Author: Marcus Boerger <helly@php.net> | + | Johannes Schlueter <johannes@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 + +extern PHP_MINIT_FUNCTION(cli_readline); +extern PHP_MSHUTDOWN_FUNCTION(cli_readline); +extern PHP_MINFO_FUNCTION(cli_readline); + +ZEND_EXTERN_MODULE_GLOBALS(cli_readline) + + diff --git a/ext/readline/tests/readline_add_history_001.phpt b/ext/readline/tests/readline_add_history_001.phpt new file mode 100644 index 0000000..a4bd524 --- /dev/null +++ b/ext/readline/tests/readline_add_history_001.phpt @@ -0,0 +1,32 @@ +--TEST-- +readline_add_history(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline") || !function_exists('readline_list_history')) die("skip"); ?> +--FILE-- +<?php + +var_dump(readline_add_history('foo')); +var_dump(readline_list_history()); +var_dump(readline_add_history(NULL)); +var_dump(readline_list_history()); +var_dump(readline_clear_history()); +var_dump(readline_add_history()); + +?> +--EXPECTF-- +bool(true) +array(1) { + [0]=> + string(3) "foo" +} +bool(true) +array(2) { + [0]=> + string(3) "foo" + [1]=> + string(0) "" +} +bool(true) + +Warning: readline_add_history() expects exactly 1 parameter, 0 given in %s on line %d +NULL diff --git a/ext/readline/tests/readline_callback_handler_install_001.phpt b/ext/readline/tests/readline_callback_handler_install_001.phpt new file mode 100644 index 0000000..8bf1d61 --- /dev/null +++ b/ext/readline/tests/readline_callback_handler_install_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +readline_callback_handler_install(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline") || !function_exists('readline_callback_handler_install')) die("skip"); ?> +--FILE-- +<?php + +function foo() { + readline_callback_handler_remove(); +} + +var_dump(readline_callback_handler_install('testing: ', 'foo')); +var_dump(readline_callback_handler_install('testing: ', 'foobar!')); +var_dump(readline_callback_handler_install('testing: ')); + +?> +--EXPECTF-- +testing: bool(true) + +Warning: readline_callback_handler_install(): foobar! is not callable in %s on line %d +bool(false) + +Warning: readline_callback_handler_install() expects exactly 2 parameters, 1 given in %s on line %d +NULL diff --git a/ext/readline/tests/readline_callback_handler_remove_001.phpt b/ext/readline/tests/readline_callback_handler_remove_001.phpt new file mode 100644 index 0000000..768041a --- /dev/null +++ b/ext/readline/tests/readline_callback_handler_remove_001.phpt @@ -0,0 +1,20 @@ +--TEST-- +readline_callback_handler_remove(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline") || !function_exists('readline_callback_handler_remove')) die("skip"); ?> +--FILE-- +<?php + +var_dump(readline_callback_handler_remove()); +var_dump(readline_callback_handler_install('testing: ', 'foo')); + +function foo() { } +var_dump(readline_callback_handler_install('testing: ', 'foo')); +var_dump(readline_callback_handler_remove()); + +?> +--EXPECT-- +bool(false) +testing: bool(true) +testing: bool(true) +bool(true) diff --git a/ext/readline/tests/readline_clear_history_001.phpt b/ext/readline/tests/readline_clear_history_001.phpt new file mode 100644 index 0000000..5df1ae7 --- /dev/null +++ b/ext/readline/tests/readline_clear_history_001.phpt @@ -0,0 +1,16 @@ +--TEST-- +readline_clear_history(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline")) die("skip"); ?> +--FILE-- +<?php + +var_dump(readline_clear_history()); +var_dump(readline_clear_history(1)); + +?> +--EXPECTF-- +bool(true) + +Warning: readline_clear_history() expects exactly 0 parameters, 1 given in %s on line %d +NULL diff --git a/ext/readline/tests/readline_completion_function_001.phpt b/ext/readline/tests/readline_completion_function_001.phpt new file mode 100644 index 0000000..828636c --- /dev/null +++ b/ext/readline/tests/readline_completion_function_001.phpt @@ -0,0 +1,25 @@ +--TEST-- +readline_completion_function(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline")) die("skip"); ?> +--FILE-- +<?php + +function foo() { } + +$data = array( + 'foo', + 'strtolower', + 1, + 1.1231 +); + +foreach ($data as $callback) { + readline_completion_function($callback); +} + +?> +--EXPECTF-- +Warning: readline_completion_function(): 1 is not callable in %s on line %d + +Warning: readline_completion_function(): 1.1231 is not callable in %s on line %d diff --git a/ext/readline/tests/readline_info_001.phpt b/ext/readline/tests/readline_info_001.phpt new file mode 100644 index 0000000..c7bc92a --- /dev/null +++ b/ext/readline/tests/readline_info_001.phpt @@ -0,0 +1,45 @@ +--TEST-- +readline_info(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline")) die("skip"); ?> +--FILE-- +<?php + +var_dump(readline_info()); +var_dump(readline_info(1)); +var_dump(readline_info(1,1)); +var_dump(readline_info('line_buffer')); +var_dump(readline_info('readline_name')); +var_dump(readline_info('readline_name', 1)); +var_dump(readline_info('readline_name')); + +?> +--EXPECTF-- +array(10) { + ["line_buffer"]=> + string(0) "" + ["point"]=> + int(0) + ["end"]=> + int(0) + ["mark"]=> + int(0) + ["done"]=> + int(0) + ["pending_input"]=> + int(0) + ["prompt"]=> + string(0) "" + ["terminal_name"]=> + string(0) "" + ["library_version"]=> + string(%d) "%s" + ["readline_name"]=> + string(5) "other" +} +NULL +NULL +string(0) "" +string(5) "other" +string(5) "other" +string(1) "1" diff --git a/ext/readline/tests/readline_list_history_001.phpt b/ext/readline/tests/readline_list_history_001.phpt new file mode 100644 index 0000000..2f6f952 --- /dev/null +++ b/ext/readline/tests/readline_list_history_001.phpt @@ -0,0 +1,17 @@ +--TEST-- +readline_list_history(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline") || !function_exists('readline_list_history')) die("skip"); ?> +--FILE-- +<?php + +var_dump(readline_list_history()); +var_dump(readline_list_history(1)); + +?> +--EXPECTF-- +array(0) { +} + +Warning: readline_list_history() expects exactly 0 parameters, 1 given in %s on line %d +NULL diff --git a/ext/readline/tests/readline_read_history_001.phpt b/ext/readline/tests/readline_read_history_001.phpt new file mode 100644 index 0000000..fcdb1ae --- /dev/null +++ b/ext/readline/tests/readline_read_history_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +readline_read_history(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline") || !function_exists('readline_list_history')) die("skip"); ?> +--FILE-- +<?php + +$name = tempnam('/tmp', 'readline.tmp'); + +readline_add_history("foo\n"); + +var_dump(readline_write_history($name)); + +var_dump(readline_clear_history()); + +var_dump(readline_read_history($name)); + +var_dump(readline_list_history()); + +unlink($name); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +array(1) { + [0]=> + string(3) "foo" +} diff --git a/ext/readline/tests/readline_write_history_001.phpt b/ext/readline/tests/readline_write_history_001.phpt new file mode 100644 index 0000000..fc0ae32 --- /dev/null +++ b/ext/readline/tests/readline_write_history_001.phpt @@ -0,0 +1,26 @@ +--TEST-- +readline_write_history(): Basic test +--SKIPIF-- +<?php if (!extension_loaded("readline") || !function_exists('readline_add_history')) die("skip"); ?> +--FILE-- +<?php + +$name = tempnam('/tmp', 'readline.tmp'); + +readline_add_history('foo'); +readline_add_history(''); +readline_add_history(1); +readline_add_history(NULL); +readline_write_history($name); + +var_dump(file_get_contents($name)); + +unlink($name); + +?> +--EXPECT-- +string(8) "foo + +1 + +" |