All,
I was recently involved in a project that relied heavily on readline to provide
console text input capabilities. However I soon noticed that the current
readline extension has a serious bug and is lacking some important
functionality.
This patch applies only to PHP compiled with libreadline (and don't have an
effect if compiled with libeditline). It adds/modifies/fixes the following:
- Fixes memory corruption caused by directly setting rl_line_buffer to new
buffer and not using rl_replace_line().
- Fixes a bad combination of preprocessor #if that would cause
readline_on_new_line() implementation not to compile when using libreadline
only.
- Adds support for rl_bind_key function by exposing:
function readline_bind_key_function($key, $callback_func)
where:
$key: Key code to bind to.
$callback_func: A callback function in the form:
function callback($key, $count) where $key and $count are the same parameters
passed from rl_bind_key()
Setting $callback_func to null will cause the binding to be removed for $key.
- Modifies the behavior of readline_info() to allow for setting the readline
properties "point" and "end" (it used to only read them but not set them).
Patch below:
--- php-5.4.0/ext/readline/readline.c 2012-01-01 15:15:04.000000000 +0200
+++ php-5.4.0-patched/ext/readline/readline.c 2012-03-19 09:09:27.388174973
+0200
@@ -58,9 +58,13 @@ PHP_FUNCTION(readline_callback_read_char
PHP_FUNCTION(readline_callback_handler_remove);
PHP_FUNCTION(readline_redisplay);
PHP_FUNCTION(readline_on_new_line);
+PHP_FUNCTION(readline_bind_key_function);
static zval *_prepped_callback = NULL;
+#endif
+#if HAVE_LIBREADLINE
+static HashTable _bind_key_functions_ht;
#endif
static zval *_readline_completion = NULL;
@@ -121,10 +125,19 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0)
ZEND_END_ARG_INFO()
+#endif
+#if HAVE_RL_ON_NEW_LINE | HAVE_LIBREADLINE
ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0)
ZEND_END_ARG_INFO()
#endif
+
+#if HAVE_LIBREADLINE
+ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_bind_key_function, 0, 0, 2)
+ ZEND_ARG_INFO(0, key)
+ ZEND_ARG_INFO(0, funcname)
+ZEND_END_ARG_INFO()
+#endif
/* }}} */
/* {{{ module stuff */
@@ -145,9 +158,12 @@ static const zend_function_entry php_rea
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
+#if HAVE_RL_ON_NEW_LINE | HAVE_LIBREADLINE
PHP_FE(readline_on_new_line, arginfo_readline_on_new_line)
#endif
+#if HAVE_LIBREADLINE
+ PHP_FE(readline_bind_key_function, arginfo_readline_bind_key_function)
+#endif
PHP_FE_END
};
@@ -170,6 +186,9 @@ ZEND_GET_MODULE(readline)
PHP_MINIT_FUNCTION(readline)
{
+#if HAVE_LIBREADLINE
+ zend_hash_init(&_bind_key_functions_ht, 255, NULL, ZVAL_PTR_DTOR, 1);
+#endif
using_history();
return PHP_MINIT(cli_readline)(INIT_FUNC_ARGS_PASSTHRU);
}
@@ -193,6 +212,9 @@ PHP_RSHUTDOWN_FUNCTION(readline)
}
#endif
+#if HAVE_LIBREADLINE
+ zend_hash_destroy(&_bind_key_functions_ht);
+#endif
return SUCCESS;
}
@@ -265,12 +287,20 @@ PHP_FUNCTION(readline_info)
if (value) {
/* XXX if (rl_line_buffer)
free(rl_line_buffer); */
convert_to_string_ex(value);
- rl_line_buffer = strdup(Z_STRVAL_PP(value));
+ rl_replace_line(Z_STRVAL_PP(value), 1);
}
RETVAL_STRING(SAFE_STRING(oldstr),1);
} else if (!strcasecmp(what, "point")) {
+ if (value) {
+ convert_to_long_ex(value);
+ rl_point = Z_LVAL_PP(value);
+ }
RETVAL_LONG(rl_point);
} else if (!strcasecmp(what, "end")) {
+ if (value) {
+ convert_to_long_ex(value);
+ rl_end = Z_LVAL_PP(value);
+ }
RETVAL_LONG(rl_end);
#ifdef HAVE_LIBREADLINE
} else if (!strcasecmp(what, "mark")) {
@@ -621,7 +651,7 @@ PHP_FUNCTION(readline_redisplay)
#endif
-#if HAVE_RL_ON_NEW_LINE
+#if HAVE_RL_ON_NEW_LINE | HAVE_LIBREADLINE
/* {{{ proto void readline_on_new_line(void)
Inform readline that the cursor has moved to a new line */
PHP_FUNCTION(readline_on_new_line)
@@ -632,6 +662,72 @@ PHP_FUNCTION(readline_on_new_line)
#endif
+/* {{{ proto bool readline_bind_key_function(string funcname)
+ Readline rl_bind_key */
+
+static int _readline_bind_key_cb(int count, int key)
+{
+ zval *params[2];
+ TSRMLS_FETCH();
+
+ params[0]=_readline_long_zval(key);
+ params[1]=_readline_long_zval(count);
+
+ zval **_callback_func = NULL;
+ long _key = key;
+ int r = zend_hash_find(&_bind_key_functions_ht, (char *) &_key,
sizeof(_key), (void **)&_callback_func);
+ if (r == -1)
+ return -1;
+
+ int _return_int = 0;
+ zval *_callback_return = NULL;
+ MAKE_STD_ZVAL(_callback_return);
+
+ if (call_user_function(CG(function_table), NULL, *_callback_func,
_callback_return, 2, params TSRMLS_CC) == SUCCESS) {
+ _return_int = _callback_return->value.lval;
+ zval_dtor(_callback_return);
+ FREE_ZVAL(_callback_return);
+ }
+
+ return _return_int;
+}
+
+PHP_FUNCTION(readline_bind_key_function)
+{
+ long key;
+ zval *arg = NULL;
+ char *name = NULL;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l!z",
&key, &arg)) {
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_P(arg) == IS_NULL) {
+ zend_hash_del(&_bind_key_functions_ht, (char *)&key,
sizeof(key));
+ rl_bind_key(key, rl_insert);
+ }
+ else {
+ 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);
+
+ zval *_temp_callback = NULL;
+ MAKE_STD_ZVAL(_temp_callback);
+ *_temp_callback = *arg;
+ zval_copy_ctor(_temp_callback);
+
+ zend_hash_add(&_bind_key_functions_ht, (char *)&key,
sizeof(key), &_temp_callback, sizeof(zval *), NULL);
+
+ rl_bind_key(key, &_readline_bind_key_cb);
+ }
+
+ RETURN_TRUE;
+}
+
+/* }}} */
#endif /* HAVE_LIBREADLINE */
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php