Back to index

php5  5.3.10
readline.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Thies C. Arntzen <thies@thieso.net>                          |
00016    +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: readline.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 /* {{{ includes & prototypes */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 #include "php.h"
00028 #include "php_readline.h"
00029 
00030 #if HAVE_LIBREADLINE || HAVE_LIBEDIT
00031 
00032 #ifndef HAVE_RL_COMPLETION_MATCHES
00033 #define rl_completion_matches completion_matches
00034 #endif
00035 
00036 #ifdef HAVE_LIBEDIT
00037 #include <editline/readline.h>
00038 #else
00039 #include <readline/readline.h>
00040 #include <readline/history.h>
00041 #endif
00042 
00043 PHP_FUNCTION(readline);
00044 PHP_FUNCTION(readline_add_history);
00045 PHP_FUNCTION(readline_info);
00046 PHP_FUNCTION(readline_clear_history);
00047 #ifndef HAVE_LIBEDIT
00048 PHP_FUNCTION(readline_list_history);
00049 #endif
00050 PHP_FUNCTION(readline_read_history);
00051 PHP_FUNCTION(readline_write_history);
00052 PHP_FUNCTION(readline_completion_function);
00053 
00054 #if HAVE_RL_CALLBACK_READ_CHAR
00055 PHP_FUNCTION(readline_callback_handler_install);
00056 PHP_FUNCTION(readline_callback_read_char);
00057 PHP_FUNCTION(readline_callback_handler_remove);
00058 PHP_FUNCTION(readline_redisplay);
00059 PHP_FUNCTION(readline_on_new_line);
00060 
00061 static zval *_prepped_callback = NULL;
00062 
00063 #endif
00064 
00065 static zval *_readline_completion = NULL;
00066 static zval _readline_array;
00067 
00068 PHP_MINIT_FUNCTION(readline);
00069 PHP_RSHUTDOWN_FUNCTION(readline);
00070 
00071 /* }}} */
00072 
00073 /* {{{ arginfo */
00074 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline, 0, 0, 0)
00075        ZEND_ARG_INFO(0, prompt)
00076 ZEND_END_ARG_INFO()
00077 
00078 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_info, 0, 0, 0)
00079        ZEND_ARG_INFO(0, varname)
00080        ZEND_ARG_INFO(0, newvalue)
00081 ZEND_END_ARG_INFO()
00082 
00083 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_add_history, 0, 0, 1)
00084        ZEND_ARG_INFO(0, prompt)
00085 ZEND_END_ARG_INFO()
00086 
00087 ZEND_BEGIN_ARG_INFO(arginfo_readline_clear_history, 0)
00088 ZEND_END_ARG_INFO()
00089 
00090 #ifndef HAVE_LIBEDIT
00091 ZEND_BEGIN_ARG_INFO(arginfo_readline_list_history, 0)
00092 ZEND_END_ARG_INFO()
00093 #endif
00094 
00095 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_read_history, 0, 0, 0)
00096        ZEND_ARG_INFO(0, filename)
00097 ZEND_END_ARG_INFO()
00098 
00099 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_write_history, 0, 0, 0)
00100        ZEND_ARG_INFO(0, filename)
00101 ZEND_END_ARG_INFO()
00102 
00103 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_completion_function, 0, 0, 1)
00104        ZEND_ARG_INFO(0, funcname)
00105 ZEND_END_ARG_INFO()
00106 
00107 #if HAVE_RL_CALLBACK_READ_CHAR
00108 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_callback_handler_install, 0, 0, 2)
00109        ZEND_ARG_INFO(0, prompt)
00110        ZEND_ARG_INFO(0, callback)
00111 ZEND_END_ARG_INFO()
00112 
00113 ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_read_char, 0)
00114 ZEND_END_ARG_INFO()
00115 
00116 ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_handler_remove, 0)
00117 ZEND_END_ARG_INFO()
00118 
00119 ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0)
00120 ZEND_END_ARG_INFO()
00121 
00122 ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0)
00123 ZEND_END_ARG_INFO()
00124 #endif
00125 /* }}} */
00126 
00127 /* {{{ module stuff */
00128 static const zend_function_entry php_readline_functions[] = {
00129        PHP_FE(readline,                           arginfo_readline)
00130        PHP_FE(readline_info,                   arginfo_readline_info)
00131        PHP_FE(readline_add_history,              arginfo_readline_add_history)
00132        PHP_FE(readline_clear_history,            arginfo_readline_clear_history)
00133 #ifndef HAVE_LIBEDIT
00134        PHP_FE(readline_list_history,             arginfo_readline_list_history)
00135 #endif
00136        PHP_FE(readline_read_history,             arginfo_readline_read_history)
00137        PHP_FE(readline_write_history,            arginfo_readline_write_history)
00138        PHP_FE(readline_completion_function,arginfo_readline_completion_function)
00139 #if HAVE_RL_CALLBACK_READ_CHAR
00140        PHP_FE(readline_callback_handler_install, arginfo_readline_callback_handler_install)
00141        PHP_FE(readline_callback_read_char,                     arginfo_readline_callback_read_char)
00142        PHP_FE(readline_callback_handler_remove,  arginfo_readline_callback_handler_remove)
00143        PHP_FE(readline_redisplay, arginfo_readline_redisplay)
00144        PHP_FE(readline_on_new_line, arginfo_readline_on_new_line)
00145 #endif
00146        PHP_FE_END
00147 };
00148 
00149 zend_module_entry readline_module_entry = { 
00150        STANDARD_MODULE_HEADER,
00151        "readline", 
00152        php_readline_functions, 
00153        PHP_MINIT(readline), 
00154        NULL,
00155        NULL,
00156        PHP_RSHUTDOWN(readline),
00157        NULL, 
00158        NO_VERSION_YET,
00159        STANDARD_MODULE_PROPERTIES
00160 };
00161 
00162 #ifdef COMPILE_DL_READLINE
00163 ZEND_GET_MODULE(readline)
00164 #endif
00165 
00166 PHP_MINIT_FUNCTION(readline)
00167 {
00168        using_history();
00169        return SUCCESS;
00170 }
00171 
00172 PHP_RSHUTDOWN_FUNCTION(readline)
00173 {
00174        if (_readline_completion) {
00175               zval_dtor(_readline_completion);
00176               FREE_ZVAL(_readline_completion);
00177        }
00178 #if HAVE_RL_CALLBACK_READ_CHAR
00179        if (_prepped_callback) {
00180               rl_callback_handler_remove();
00181               zval_ptr_dtor(&_prepped_callback);
00182               _prepped_callback = 0;
00183        }
00184 #endif
00185 
00186        return SUCCESS;
00187 }
00188 
00189 /* }}} */
00190 
00191 /* {{{ proto string readline([string prompt]) 
00192    Reads a line */
00193 PHP_FUNCTION(readline)
00194 {
00195        char *prompt = NULL;
00196        int prompt_len;
00197        char *result;
00198 
00199        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &prompt, &prompt_len)) {
00200               RETURN_FALSE;
00201        }
00202 
00203        result = readline(prompt);
00204 
00205        if (! result) {
00206               RETURN_FALSE;
00207        } else {
00208               RETVAL_STRING(result,1);
00209               free(result);
00210        }
00211 }
00212 
00213 /* }}} */
00214 
00215 #define SAFE_STRING(s) ((s)?(char*)(s):"")
00216 
00217 /* {{{ proto mixed readline_info([string varname [, string newvalue]]) 
00218    Gets/sets various internal readline variables. */
00219 PHP_FUNCTION(readline_info)
00220 {
00221        char *what = NULL;
00222        zval **value = NULL;
00223        int what_len, oldval;
00224        char *oldstr;
00225 
00226        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sZ", &what, &what_len, &value) == FAILURE) {
00227               return;
00228        }
00229 
00230        if (!what) {
00231               array_init(return_value);
00232               add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer),1);
00233               add_assoc_long(return_value,"point",rl_point);
00234               add_assoc_long(return_value,"end",rl_end);
00235 #ifdef HAVE_LIBREADLINE
00236               add_assoc_long(return_value,"mark",rl_mark);
00237               add_assoc_long(return_value,"done",rl_done);
00238               add_assoc_long(return_value,"pending_input",rl_pending_input);
00239               add_assoc_string(return_value,"prompt",SAFE_STRING(rl_prompt),1);
00240               add_assoc_string(return_value,"terminal_name",(char *)SAFE_STRING(rl_terminal_name),1);
00241 #endif
00242 #if HAVE_ERASE_EMPTY_LINE
00243               add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line);
00244 #endif
00245               add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version),1);
00246               add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name),1);
00247        } else {
00248               if (!strcasecmp(what,"line_buffer")) {
00249                      oldstr = rl_line_buffer;
00250                      if (value) {
00251                             /* XXX if (rl_line_buffer) free(rl_line_buffer); */
00252                             convert_to_string_ex(value);
00253                             rl_line_buffer = strdup(Z_STRVAL_PP(value));
00254                      }
00255                      RETVAL_STRING(SAFE_STRING(oldstr),1);
00256               } else if (!strcasecmp(what, "point")) {
00257                      RETVAL_LONG(rl_point);
00258               } else if (!strcasecmp(what, "end")) {
00259                      RETVAL_LONG(rl_end);
00260 #ifdef HAVE_LIBREADLINE
00261               } else if (!strcasecmp(what, "mark")) {
00262                      RETVAL_LONG(rl_mark);
00263               } else if (!strcasecmp(what, "done")) {
00264                      oldval = rl_done;
00265                      if (value) {
00266                             convert_to_long_ex(value);
00267                             rl_done = Z_LVAL_PP(value);
00268                      }
00269                      RETVAL_LONG(oldval);
00270               } else if (!strcasecmp(what, "pending_input")) {
00271                      oldval = rl_pending_input;
00272                      if (value) {
00273                             convert_to_string_ex(value);
00274                             rl_pending_input = Z_STRVAL_PP(value)[0];
00275                      }
00276                      RETVAL_LONG(oldval);
00277               } else if (!strcasecmp(what, "prompt")) {
00278                      RETVAL_STRING(SAFE_STRING(rl_prompt),1);
00279               } else if (!strcasecmp(what, "terminal_name")) {
00280                      RETVAL_STRING((char *)SAFE_STRING(rl_terminal_name),1);
00281 #endif
00282 #if HAVE_ERASE_EMPTY_LINE
00283               } else if (!strcasecmp(what, "erase_empty_line")) {
00284                      oldval = rl_erase_empty_line;
00285                      if (value) {
00286                             convert_to_long_ex(value);
00287                             rl_erase_empty_line = Z_LVAL_PP(value);
00288                      }
00289                      RETVAL_LONG(oldval);
00290 #endif
00291               } else if (!strcasecmp(what,"library_version")) {
00292                      RETVAL_STRING((char *)SAFE_STRING(rl_library_version),1);
00293               } else if (!strcasecmp(what, "readline_name")) {
00294                      oldstr = (char*)rl_readline_name;
00295                      if (value) {
00296                             /* XXX if (rl_readline_name) free(rl_readline_name); */
00297                             convert_to_string_ex(value);
00298                             rl_readline_name = strdup(Z_STRVAL_PP(value));;
00299                      }
00300                      RETVAL_STRING(SAFE_STRING(oldstr),1);
00301               } 
00302        }
00303 }
00304 
00305 /* }}} */
00306 /* {{{ proto bool readline_add_history(string prompt) 
00307    Adds a line to the history */
00308 PHP_FUNCTION(readline_add_history)
00309 {
00310        char *arg;
00311        int arg_len;
00312 
00313        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
00314               return;
00315        }
00316 
00317        add_history(arg);
00318 
00319        RETURN_TRUE;
00320 }
00321 
00322 /* }}} */
00323 /* {{{ proto bool readline_clear_history(void) 
00324    Clears the history */
00325 PHP_FUNCTION(readline_clear_history)
00326 {
00327        if (zend_parse_parameters_none() == FAILURE) {
00328               return;
00329        }
00330 
00331        clear_history();
00332 
00333        RETURN_TRUE;
00334 }
00335 
00336 /* }}} */
00337 /* {{{ proto array readline_list_history(void) 
00338    Lists the history */
00339 #ifndef HAVE_LIBEDIT
00340 PHP_FUNCTION(readline_list_history)
00341 {
00342        HIST_ENTRY **history;
00343 
00344        if (zend_parse_parameters_none() == FAILURE) {
00345               return;
00346        }
00347        
00348        history = history_list();
00349        
00350        array_init(return_value);
00351 
00352        if (history) {
00353               int i;
00354               for (i = 0; history[i]; i++) {
00355                      add_next_index_string(return_value,history[i]->line,1);
00356               }
00357        }
00358 }
00359 #endif
00360 /* }}} */
00361 /* {{{ proto bool readline_read_history([string filename]) 
00362    Reads the history */
00363 PHP_FUNCTION(readline_read_history)
00364 {
00365        char *arg = NULL;
00366        int arg_len;
00367 
00368        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &arg, &arg_len) == FAILURE) {
00369               return;
00370        }
00371 
00372        /* XXX from & to NYI */
00373        if (read_history(arg)) {
00374               RETURN_FALSE;
00375        } else {
00376               RETURN_TRUE;
00377        }
00378 }
00379 
00380 /* }}} */
00381 /* {{{ proto bool readline_write_history([string filename]) 
00382    Writes the history */
00383 PHP_FUNCTION(readline_write_history)
00384 {
00385        char *arg = NULL;
00386        int arg_len;
00387 
00388        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &arg, &arg_len) == FAILURE) {
00389               return;
00390        }
00391 
00392        if (write_history(arg)) {
00393               RETURN_FALSE;
00394        } else {
00395               RETURN_TRUE;
00396        }
00397 }
00398 
00399 /* }}} */
00400 /* {{{ proto bool readline_completion_function(string funcname) 
00401    Readline completion function? */
00402 
00403 static char *_readline_command_generator(const char *text, int state)
00404 {
00405        HashTable  *myht = Z_ARRVAL(_readline_array);
00406        zval **entry;
00407        
00408        if (!state) {
00409               zend_hash_internal_pointer_reset(myht);
00410        }
00411        
00412        while (zend_hash_get_current_data(myht, (void **)&entry) == SUCCESS) {
00413               zend_hash_move_forward(myht);
00414 
00415               convert_to_string_ex(entry);
00416               if (strncmp (Z_STRVAL_PP(entry), text, strlen(text)) == 0) {
00417                      return (strdup(Z_STRVAL_PP(entry)));
00418               }
00419        }
00420 
00421        return NULL;
00422 }
00423 
00424 static zval *_readline_string_zval(const char *str)
00425 {
00426        zval *ret;
00427        int len;
00428        
00429        MAKE_STD_ZVAL(ret);
00430        
00431        if (str) {
00432               len = strlen(str);
00433               ZVAL_STRINGL(ret, (char*)str, len, 1);
00434        } else {
00435               ZVAL_NULL(ret);
00436        }
00437 
00438        return ret;
00439 }
00440 
00441 static zval *_readline_long_zval(long l)
00442 {
00443        zval *ret;
00444        MAKE_STD_ZVAL(ret);
00445 
00446        Z_TYPE_P(ret) = IS_LONG;
00447        Z_LVAL_P(ret) = l;
00448        return ret;
00449 }
00450 
00451 static char **_readline_completion_cb(const char *text, int start, int end)
00452 { 
00453        zval *params[3];
00454        int i;
00455        char **matches = NULL;
00456        TSRMLS_FETCH();
00457 
00458        params[0]=_readline_string_zval(text);
00459        params[1]=_readline_long_zval(start);
00460        params[2]=_readline_long_zval(end);
00461 
00462        if (call_user_function(CG(function_table), NULL, _readline_completion, &_readline_array, 3, params TSRMLS_CC) == SUCCESS) {
00463               if (Z_TYPE(_readline_array) == IS_ARRAY) {
00464                      if (zend_hash_num_elements(Z_ARRVAL(_readline_array))) {
00465                             matches = rl_completion_matches(text,_readline_command_generator);
00466                      } else {
00467                             matches = malloc(sizeof(char *) * 2);
00468                             if (!matches) {
00469                                    return NULL;
00470                             }
00471                             matches[0] = strdup("");
00472                             matches[1] = '\0';
00473                      }
00474               }
00475        }
00476        
00477        for (i = 0; i < 3; i++) {
00478               zval_ptr_dtor(&params[i]);
00479        }
00480        zval_dtor(&_readline_array);
00481        
00482        return matches; 
00483 }
00484 
00485 PHP_FUNCTION(readline_completion_function)
00486 {
00487        zval *arg = NULL;
00488        char *name = NULL;
00489 
00490        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg)) {
00491               RETURN_FALSE;
00492        }
00493 
00494        if (!zend_is_callable(arg, 0, &name TSRMLS_CC)) {
00495               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name);
00496               efree(name);
00497               RETURN_FALSE;
00498        }
00499        efree(name);
00500 
00501        if (_readline_completion) {
00502               zval_dtor(_readline_completion);
00503               FREE_ZVAL(_readline_completion);
00504        }
00505 
00506        MAKE_STD_ZVAL(_readline_completion);
00507        *_readline_completion = *arg;
00508        zval_copy_ctor(_readline_completion);
00509 
00510        rl_attempted_completion_function = _readline_completion_cb;
00511        if (rl_attempted_completion_function == NULL) {
00512               efree(name);
00513               RETURN_FALSE;
00514        }
00515        RETURN_TRUE;
00516 }
00517 
00518 /* }}} */
00519 
00520 #if HAVE_RL_CALLBACK_READ_CHAR
00521 
00522 static void php_rl_callback_handler(char *the_line)
00523 {
00524        zval *params[1];
00525        zval dummy;
00526        TSRMLS_FETCH();
00527 
00528        ZVAL_NULL(&dummy);
00529 
00530        params[0] = _readline_string_zval(the_line);
00531 
00532        call_user_function(CG(function_table), NULL, _prepped_callback, &dummy, 1, params TSRMLS_CC);
00533 
00534        zval_ptr_dtor(&params[0]);
00535        zval_dtor(&dummy);
00536 }
00537 
00538 /* {{{ proto void readline_callback_handler_install(string prompt, mixed callback)
00539    Initializes the readline callback interface and terminal, prints the prompt and returns immediately */
00540 PHP_FUNCTION(readline_callback_handler_install)
00541 {
00542        zval *callback;
00543        char *name = NULL;
00544        char *prompt;
00545        int prompt_len;
00546 
00547        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &prompt, &prompt_len, &callback)) {
00548               return;
00549        }
00550 
00551        if (!zend_is_callable(callback, 0, &name TSRMLS_CC)) {
00552               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name);
00553               efree(name);
00554               RETURN_FALSE;
00555        }
00556        efree(name);
00557 
00558        if (_prepped_callback) {
00559               rl_callback_handler_remove();
00560               zval_dtor(_prepped_callback);
00561               FREE_ZVAL(_prepped_callback);
00562        }
00563 
00564        MAKE_STD_ZVAL(_prepped_callback);
00565        *_prepped_callback = *callback;
00566        zval_copy_ctor(_prepped_callback);
00567 
00568        rl_callback_handler_install(prompt, php_rl_callback_handler);
00569 
00570        RETURN_TRUE;
00571 }
00572 /* }}} */
00573 
00574 /* {{{ proto void readline_callback_read_char()
00575    Informs the readline callback interface that a character is ready for input */
00576 PHP_FUNCTION(readline_callback_read_char)
00577 {
00578        if (_prepped_callback) {
00579               rl_callback_read_char();
00580        }
00581 }
00582 /* }}} */
00583 
00584 /* {{{ proto bool readline_callback_handler_remove()
00585    Removes a previously installed callback handler and restores terminal settings */
00586 PHP_FUNCTION(readline_callback_handler_remove)
00587 {
00588        if (_prepped_callback) {
00589               rl_callback_handler_remove();
00590               zval_dtor(_prepped_callback);
00591               FREE_ZVAL(_prepped_callback);
00592               _prepped_callback = 0;
00593               RETURN_TRUE;
00594        }
00595        RETURN_FALSE;
00596 }
00597 /* }}} */
00598 
00599 /* {{{ proto void readline_redisplay(void)
00600    Ask readline to redraw the display */
00601 PHP_FUNCTION(readline_redisplay)
00602 {
00603        rl_redisplay();
00604 }
00605 /* }}} */
00606 
00607 /* {{{ proto void readline_on_new_line(void)
00608    Inform readline that the cursor has moved to a new line */
00609 PHP_FUNCTION(readline_on_new_line)
00610 {
00611        rl_on_new_line();
00612 }
00613 /* }}} */
00614 
00615 #endif
00616 
00617 
00618 #endif /* HAVE_LIBREADLINE */
00619 
00620 /*
00621  * Local variables:
00622  * tab-width: 4
00623  * c-basic-offset: 4
00624  * End:
00625  */