1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Author: Thies C. Arntzen <thies@thieso.net> | 16 +----------------------------------------------------------------------+ 17*/ 18 19/* $Id$ */ 20 21/* {{{ includes & prototypes */ 22 23#ifdef HAVE_CONFIG_H 24#include "config.h" 25#endif 26 27#include "php.h" 28#include "php_readline.h" 29#include "readline_cli.h" 30 31#if HAVE_LIBREADLINE || HAVE_LIBEDIT 32 33#ifndef HAVE_RL_COMPLETION_MATCHES 34#define rl_completion_matches completion_matches 35#endif 36 37#ifdef HAVE_LIBEDIT 38#include <editline/readline.h> 39#else 40#include <readline/readline.h> 41#include <readline/history.h> 42#endif 43 44PHP_FUNCTION(readline); 45PHP_FUNCTION(readline_add_history); 46PHP_FUNCTION(readline_info); 47PHP_FUNCTION(readline_clear_history); 48#ifndef HAVE_LIBEDIT 49PHP_FUNCTION(readline_list_history); 50#endif 51PHP_FUNCTION(readline_read_history); 52PHP_FUNCTION(readline_write_history); 53PHP_FUNCTION(readline_completion_function); 54 55#if HAVE_RL_CALLBACK_READ_CHAR 56PHP_FUNCTION(readline_callback_handler_install); 57PHP_FUNCTION(readline_callback_read_char); 58PHP_FUNCTION(readline_callback_handler_remove); 59PHP_FUNCTION(readline_redisplay); 60PHP_FUNCTION(readline_on_new_line); 61 62static zval *_prepped_callback = NULL; 63 64#endif 65 66static zval *_readline_completion = NULL; 67static zval _readline_array; 68 69PHP_MINIT_FUNCTION(readline); 70PHP_MSHUTDOWN_FUNCTION(readline); 71PHP_RSHUTDOWN_FUNCTION(readline); 72PHP_MINFO_FUNCTION(readline); 73 74/* }}} */ 75 76/* {{{ arginfo */ 77ZEND_BEGIN_ARG_INFO_EX(arginfo_readline, 0, 0, 0) 78 ZEND_ARG_INFO(0, prompt) 79ZEND_END_ARG_INFO() 80 81ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_info, 0, 0, 0) 82 ZEND_ARG_INFO(0, varname) 83 ZEND_ARG_INFO(0, newvalue) 84ZEND_END_ARG_INFO() 85 86ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_add_history, 0, 0, 1) 87 ZEND_ARG_INFO(0, prompt) 88ZEND_END_ARG_INFO() 89 90ZEND_BEGIN_ARG_INFO(arginfo_readline_clear_history, 0) 91ZEND_END_ARG_INFO() 92 93#ifndef HAVE_LIBEDIT 94ZEND_BEGIN_ARG_INFO(arginfo_readline_list_history, 0) 95ZEND_END_ARG_INFO() 96#endif 97 98ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_read_history, 0, 0, 0) 99 ZEND_ARG_INFO(0, filename) 100ZEND_END_ARG_INFO() 101 102ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_write_history, 0, 0, 0) 103 ZEND_ARG_INFO(0, filename) 104ZEND_END_ARG_INFO() 105 106ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_completion_function, 0, 0, 1) 107 ZEND_ARG_INFO(0, funcname) 108ZEND_END_ARG_INFO() 109 110#if HAVE_RL_CALLBACK_READ_CHAR 111ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_callback_handler_install, 0, 0, 2) 112 ZEND_ARG_INFO(0, prompt) 113 ZEND_ARG_INFO(0, callback) 114ZEND_END_ARG_INFO() 115 116ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_read_char, 0) 117ZEND_END_ARG_INFO() 118 119ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_handler_remove, 0) 120ZEND_END_ARG_INFO() 121 122ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0) 123ZEND_END_ARG_INFO() 124 125ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0) 126ZEND_END_ARG_INFO() 127#endif 128/* }}} */ 129 130/* {{{ module stuff */ 131static const zend_function_entry php_readline_functions[] = { 132 PHP_FE(readline, arginfo_readline) 133 PHP_FE(readline_info, arginfo_readline_info) 134 PHP_FE(readline_add_history, arginfo_readline_add_history) 135 PHP_FE(readline_clear_history, arginfo_readline_clear_history) 136#ifndef HAVE_LIBEDIT 137 PHP_FE(readline_list_history, arginfo_readline_list_history) 138#endif 139 PHP_FE(readline_read_history, arginfo_readline_read_history) 140 PHP_FE(readline_write_history, arginfo_readline_write_history) 141 PHP_FE(readline_completion_function,arginfo_readline_completion_function) 142#if HAVE_RL_CALLBACK_READ_CHAR 143 PHP_FE(readline_callback_handler_install, arginfo_readline_callback_handler_install) 144 PHP_FE(readline_callback_read_char, arginfo_readline_callback_read_char) 145 PHP_FE(readline_callback_handler_remove, arginfo_readline_callback_handler_remove) 146 PHP_FE(readline_redisplay, arginfo_readline_redisplay) 147#endif 148#if HAVE_RL_ON_NEW_LINE 149 PHP_FE(readline_on_new_line, arginfo_readline_on_new_line) 150#endif 151 PHP_FE_END 152}; 153 154zend_module_entry readline_module_entry = { 155 STANDARD_MODULE_HEADER, 156 "readline", 157 php_readline_functions, 158 PHP_MINIT(readline), 159 PHP_MSHUTDOWN(readline), 160 NULL, 161 PHP_RSHUTDOWN(readline), 162 PHP_MINFO(readline), 163 PHP_VERSION, 164 STANDARD_MODULE_PROPERTIES 165}; 166 167#ifdef COMPILE_DL_READLINE 168ZEND_GET_MODULE(readline) 169#endif 170 171PHP_MINIT_FUNCTION(readline) 172{ 173 using_history(); 174 return PHP_MINIT(cli_readline)(INIT_FUNC_ARGS_PASSTHRU); 175} 176 177PHP_MSHUTDOWN_FUNCTION(readline) 178{ 179 return PHP_MSHUTDOWN(cli_readline)(SHUTDOWN_FUNC_ARGS_PASSTHRU); 180} 181 182PHP_RSHUTDOWN_FUNCTION(readline) 183{ 184 if (_readline_completion) { 185 zval_dtor(_readline_completion); 186 FREE_ZVAL(_readline_completion); 187 } 188#if HAVE_RL_CALLBACK_READ_CHAR 189 if (_prepped_callback) { 190 rl_callback_handler_remove(); 191 zval_ptr_dtor(&_prepped_callback); 192 _prepped_callback = 0; 193 } 194#endif 195 196 return SUCCESS; 197} 198 199PHP_MINFO_FUNCTION(readline) 200{ 201 PHP_MINFO(cli_readline)(ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU); 202} 203 204/* }}} */ 205 206/* {{{ proto string readline([string prompt]) 207 Reads a line */ 208PHP_FUNCTION(readline) 209{ 210 char *prompt = NULL; 211 int prompt_len; 212 char *result; 213 214 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &prompt, &prompt_len)) { 215 RETURN_FALSE; 216 } 217 218 result = readline(prompt); 219 220 if (! result) { 221 RETURN_FALSE; 222 } else { 223 RETVAL_STRING(result,1); 224 free(result); 225 } 226} 227 228/* }}} */ 229 230#define SAFE_STRING(s) ((s)?(char*)(s):"") 231 232/* {{{ proto mixed readline_info([string varname [, string newvalue]]) 233 Gets/sets various internal readline variables. */ 234PHP_FUNCTION(readline_info) 235{ 236 char *what = NULL; 237 zval **value = NULL; 238 int what_len, oldval; 239 char *oldstr; 240 241 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sZ", &what, &what_len, &value) == FAILURE) { 242 return; 243 } 244 245 if (!what) { 246 array_init(return_value); 247 add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer),1); 248 add_assoc_long(return_value,"point",rl_point); 249 add_assoc_long(return_value,"end",rl_end); 250#ifdef HAVE_LIBREADLINE 251 add_assoc_long(return_value,"mark",rl_mark); 252 add_assoc_long(return_value,"done",rl_done); 253 add_assoc_long(return_value,"pending_input",rl_pending_input); 254 add_assoc_string(return_value,"prompt",SAFE_STRING(rl_prompt),1); 255 add_assoc_string(return_value,"terminal_name",(char *)SAFE_STRING(rl_terminal_name),1); 256#endif 257#if HAVE_ERASE_EMPTY_LINE 258 add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line); 259#endif 260 add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version),1); 261 add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name),1); 262 add_assoc_long(return_value,"attempted_completion_over",rl_attempted_completion_over); 263 } else { 264 if (!strcasecmp(what,"line_buffer")) { 265 oldstr = rl_line_buffer; 266 if (value) { 267 /* XXX if (rl_line_buffer) free(rl_line_buffer); */ 268 convert_to_string_ex(value); 269 rl_line_buffer = strdup(Z_STRVAL_PP(value)); 270 } 271 RETVAL_STRING(SAFE_STRING(oldstr),1); 272 } else if (!strcasecmp(what, "point")) { 273 RETVAL_LONG(rl_point); 274 } else if (!strcasecmp(what, "end")) { 275 RETVAL_LONG(rl_end); 276#ifdef HAVE_LIBREADLINE 277 } else if (!strcasecmp(what, "mark")) { 278 RETVAL_LONG(rl_mark); 279 } else if (!strcasecmp(what, "done")) { 280 oldval = rl_done; 281 if (value) { 282 convert_to_long_ex(value); 283 rl_done = Z_LVAL_PP(value); 284 } 285 RETVAL_LONG(oldval); 286 } else if (!strcasecmp(what, "pending_input")) { 287 oldval = rl_pending_input; 288 if (value) { 289 convert_to_string_ex(value); 290 rl_pending_input = Z_STRVAL_PP(value)[0]; 291 } 292 RETVAL_LONG(oldval); 293 } else if (!strcasecmp(what, "prompt")) { 294 RETVAL_STRING(SAFE_STRING(rl_prompt),1); 295 } else if (!strcasecmp(what, "terminal_name")) { 296 RETVAL_STRING((char *)SAFE_STRING(rl_terminal_name),1); 297#endif 298#if HAVE_ERASE_EMPTY_LINE 299 } else if (!strcasecmp(what, "erase_empty_line")) { 300 oldval = rl_erase_empty_line; 301 if (value) { 302 convert_to_long_ex(value); 303 rl_erase_empty_line = Z_LVAL_PP(value); 304 } 305 RETVAL_LONG(oldval); 306#endif 307 } else if (!strcasecmp(what,"library_version")) { 308 RETVAL_STRING((char *)SAFE_STRING(rl_library_version),1); 309 } else if (!strcasecmp(what, "readline_name")) { 310 oldstr = (char*)rl_readline_name; 311 if (value) { 312 /* XXX if (rl_readline_name) free(rl_readline_name); */ 313 convert_to_string_ex(value); 314 rl_readline_name = strdup(Z_STRVAL_PP(value));; 315 } 316 RETVAL_STRING(SAFE_STRING(oldstr),1); 317 } else if (!strcasecmp(what, "attempted_completion_over")) { 318 oldval = rl_attempted_completion_over; 319 if (value) { 320 convert_to_long_ex(value); 321 rl_attempted_completion_over = Z_LVAL_PP(value); 322 } 323 RETVAL_LONG(oldval); 324 } 325 } 326} 327 328/* }}} */ 329/* {{{ proto bool readline_add_history(string prompt) 330 Adds a line to the history */ 331PHP_FUNCTION(readline_add_history) 332{ 333 char *arg; 334 int arg_len; 335 336 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { 337 return; 338 } 339 340 add_history(arg); 341 342 RETURN_TRUE; 343} 344 345/* }}} */ 346/* {{{ proto bool readline_clear_history(void) 347 Clears the history */ 348PHP_FUNCTION(readline_clear_history) 349{ 350 if (zend_parse_parameters_none() == FAILURE) { 351 return; 352 } 353 354 clear_history(); 355 356 RETURN_TRUE; 357} 358 359/* }}} */ 360/* {{{ proto array readline_list_history(void) 361 Lists the history */ 362#ifndef HAVE_LIBEDIT 363PHP_FUNCTION(readline_list_history) 364{ 365 HIST_ENTRY **history; 366 367 if (zend_parse_parameters_none() == FAILURE) { 368 return; 369 } 370 371 history = history_list(); 372 373 array_init(return_value); 374 375 if (history) { 376 int i; 377 for (i = 0; history[i]; i++) { 378 add_next_index_string(return_value,history[i]->line,1); 379 } 380 } 381} 382#endif 383/* }}} */ 384/* {{{ proto bool readline_read_history([string filename]) 385 Reads the history */ 386PHP_FUNCTION(readline_read_history) 387{ 388 char *arg = NULL; 389 int arg_len; 390 391 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|p", &arg, &arg_len) == FAILURE) { 392 return; 393 } 394 395 if (php_check_open_basedir(arg TSRMLS_CC)) { 396 RETURN_FALSE; 397 } 398 399 /* XXX from & to NYI */ 400 if (read_history(arg)) { 401 RETURN_FALSE; 402 } else { 403 RETURN_TRUE; 404 } 405} 406 407/* }}} */ 408/* {{{ proto bool readline_write_history([string filename]) 409 Writes the history */ 410PHP_FUNCTION(readline_write_history) 411{ 412 char *arg = NULL; 413 int arg_len; 414 415 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|p", &arg, &arg_len) == FAILURE) { 416 return; 417 } 418 419 if (php_check_open_basedir(arg TSRMLS_CC)) { 420 RETURN_FALSE; 421 } 422 423 if (write_history(arg)) { 424 RETURN_FALSE; 425 } else { 426 RETURN_TRUE; 427 } 428} 429 430/* }}} */ 431/* {{{ proto bool readline_completion_function(string funcname) 432 Readline completion function? */ 433 434static char *_readline_command_generator(const char *text, int state) 435{ 436 HashTable *myht = Z_ARRVAL(_readline_array); 437 zval **entry; 438 439 if (!state) { 440 zend_hash_internal_pointer_reset(myht); 441 } 442 443 while (zend_hash_get_current_data(myht, (void **)&entry) == SUCCESS) { 444 zend_hash_move_forward(myht); 445 446 convert_to_string_ex(entry); 447 if (strncmp (Z_STRVAL_PP(entry), text, strlen(text)) == 0) { 448 return (strdup(Z_STRVAL_PP(entry))); 449 } 450 } 451 452 return NULL; 453} 454 455static zval *_readline_string_zval(const char *str) 456{ 457 zval *ret; 458 int len; 459 460 MAKE_STD_ZVAL(ret); 461 462 if (str) { 463 len = strlen(str); 464 ZVAL_STRINGL(ret, (char*)str, len, 1); 465 } else { 466 ZVAL_NULL(ret); 467 } 468 469 return ret; 470} 471 472static zval *_readline_long_zval(long l) 473{ 474 zval *ret; 475 MAKE_STD_ZVAL(ret); 476 477 Z_TYPE_P(ret) = IS_LONG; 478 Z_LVAL_P(ret) = l; 479 return ret; 480} 481 482static char **_readline_completion_cb(const char *text, int start, int end) 483{ 484 zval *params[3]; 485 int i; 486 char **matches = NULL; 487 TSRMLS_FETCH(); 488 489 params[0]=_readline_string_zval(text); 490 params[1]=_readline_long_zval(start); 491 params[2]=_readline_long_zval(end); 492 493 if (call_user_function(CG(function_table), NULL, _readline_completion, &_readline_array, 3, params TSRMLS_CC) == SUCCESS) { 494 if (Z_TYPE(_readline_array) == IS_ARRAY) { 495 if (zend_hash_num_elements(Z_ARRVAL(_readline_array))) { 496 matches = rl_completion_matches(text,_readline_command_generator); 497 } else { 498 matches = malloc(sizeof(char *) * 2); 499 if (!matches) { 500 return NULL; 501 } 502 matches[0] = strdup(""); 503 matches[1] = '\0'; 504 } 505 } 506 } 507 508 for (i = 0; i < 3; i++) { 509 zval_ptr_dtor(¶ms[i]); 510 } 511 zval_dtor(&_readline_array); 512 513 return matches; 514} 515 516PHP_FUNCTION(readline_completion_function) 517{ 518 zval *arg = NULL; 519 char *name = NULL; 520 521 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg)) { 522 RETURN_FALSE; 523 } 524 525 if (!zend_is_callable(arg, 0, &name TSRMLS_CC)) { 526 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name); 527 efree(name); 528 RETURN_FALSE; 529 } 530 efree(name); 531 532 if (_readline_completion) { 533 zval_dtor(_readline_completion); 534 FREE_ZVAL(_readline_completion); 535 } 536 537 MAKE_STD_ZVAL(_readline_completion); 538 *_readline_completion = *arg; 539 zval_copy_ctor(_readline_completion); 540 541 rl_attempted_completion_function = _readline_completion_cb; 542 if (rl_attempted_completion_function == NULL) { 543 efree(name); 544 RETURN_FALSE; 545 } 546 RETURN_TRUE; 547} 548 549/* }}} */ 550 551#if HAVE_RL_CALLBACK_READ_CHAR 552 553static void php_rl_callback_handler(char *the_line) 554{ 555 zval *params[1]; 556 zval dummy; 557 TSRMLS_FETCH(); 558 559 ZVAL_NULL(&dummy); 560 561 params[0] = _readline_string_zval(the_line); 562 563 call_user_function(CG(function_table), NULL, _prepped_callback, &dummy, 1, params TSRMLS_CC); 564 565 zval_ptr_dtor(¶ms[0]); 566 zval_dtor(&dummy); 567} 568 569/* {{{ proto void readline_callback_handler_install(string prompt, mixed callback) 570 Initializes the readline callback interface and terminal, prints the prompt and returns immediately */ 571PHP_FUNCTION(readline_callback_handler_install) 572{ 573 zval *callback; 574 char *name = NULL; 575 char *prompt; 576 int prompt_len; 577 578 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &prompt, &prompt_len, &callback)) { 579 return; 580 } 581 582 if (!zend_is_callable(callback, 0, &name TSRMLS_CC)) { 583 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name); 584 efree(name); 585 RETURN_FALSE; 586 } 587 efree(name); 588 589 if (_prepped_callback) { 590 rl_callback_handler_remove(); 591 zval_dtor(_prepped_callback); 592 FREE_ZVAL(_prepped_callback); 593 } 594 595 ALLOC_ZVAL(_prepped_callback); 596 MAKE_COPY_ZVAL(&callback, _prepped_callback); 597 598 rl_callback_handler_install(prompt, php_rl_callback_handler); 599 600 RETURN_TRUE; 601} 602/* }}} */ 603 604/* {{{ proto void readline_callback_read_char() 605 Informs the readline callback interface that a character is ready for input */ 606PHP_FUNCTION(readline_callback_read_char) 607{ 608 if (_prepped_callback) { 609 rl_callback_read_char(); 610 } 611} 612/* }}} */ 613 614/* {{{ proto bool readline_callback_handler_remove() 615 Removes a previously installed callback handler and restores terminal settings */ 616PHP_FUNCTION(readline_callback_handler_remove) 617{ 618 if (_prepped_callback) { 619 rl_callback_handler_remove(); 620 zval_dtor(_prepped_callback); 621 FREE_ZVAL(_prepped_callback); 622 _prepped_callback = 0; 623 RETURN_TRUE; 624 } 625 RETURN_FALSE; 626} 627/* }}} */ 628 629/* {{{ proto void readline_redisplay(void) 630 Ask readline to redraw the display */ 631PHP_FUNCTION(readline_redisplay) 632{ 633 rl_redisplay(); 634} 635/* }}} */ 636 637#endif 638 639#if HAVE_RL_ON_NEW_LINE 640/* {{{ proto void readline_on_new_line(void) 641 Inform readline that the cursor has moved to a new line */ 642PHP_FUNCTION(readline_on_new_line) 643{ 644 rl_on_new_line(); 645} 646/* }}} */ 647 648#endif 649 650 651#endif /* HAVE_LIBREADLINE */ 652 653/* 654 * Local variables: 655 * tab-width: 4 656 * c-basic-offset: 4 657 * End: 658 */ 659