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 } else { 263 if (!strcasecmp(what,"line_buffer")) { 264 oldstr = rl_line_buffer; 265 if (value) { 266 /* XXX if (rl_line_buffer) free(rl_line_buffer); */ 267 convert_to_string_ex(value); 268 rl_line_buffer = strdup(Z_STRVAL_PP(value)); 269 } 270 RETVAL_STRING(SAFE_STRING(oldstr),1); 271 } else if (!strcasecmp(what, "point")) { 272 RETVAL_LONG(rl_point); 273 } else if (!strcasecmp(what, "end")) { 274 RETVAL_LONG(rl_end); 275#ifdef HAVE_LIBREADLINE 276 } else if (!strcasecmp(what, "mark")) { 277 RETVAL_LONG(rl_mark); 278 } else if (!strcasecmp(what, "done")) { 279 oldval = rl_done; 280 if (value) { 281 convert_to_long_ex(value); 282 rl_done = Z_LVAL_PP(value); 283 } 284 RETVAL_LONG(oldval); 285 } else if (!strcasecmp(what, "pending_input")) { 286 oldval = rl_pending_input; 287 if (value) { 288 convert_to_string_ex(value); 289 rl_pending_input = Z_STRVAL_PP(value)[0]; 290 } 291 RETVAL_LONG(oldval); 292 } else if (!strcasecmp(what, "prompt")) { 293 RETVAL_STRING(SAFE_STRING(rl_prompt),1); 294 } else if (!strcasecmp(what, "terminal_name")) { 295 RETVAL_STRING((char *)SAFE_STRING(rl_terminal_name),1); 296#endif 297#if HAVE_ERASE_EMPTY_LINE 298 } else if (!strcasecmp(what, "erase_empty_line")) { 299 oldval = rl_erase_empty_line; 300 if (value) { 301 convert_to_long_ex(value); 302 rl_erase_empty_line = Z_LVAL_PP(value); 303 } 304 RETVAL_LONG(oldval); 305#endif 306 } else if (!strcasecmp(what,"library_version")) { 307 RETVAL_STRING((char *)SAFE_STRING(rl_library_version),1); 308 } else if (!strcasecmp(what, "readline_name")) { 309 oldstr = (char*)rl_readline_name; 310 if (value) { 311 /* XXX if (rl_readline_name) free(rl_readline_name); */ 312 convert_to_string_ex(value); 313 rl_readline_name = strdup(Z_STRVAL_PP(value));; 314 } 315 RETVAL_STRING(SAFE_STRING(oldstr),1); 316 } 317 } 318} 319 320/* }}} */ 321/* {{{ proto bool readline_add_history(string prompt) 322 Adds a line to the history */ 323PHP_FUNCTION(readline_add_history) 324{ 325 char *arg; 326 int arg_len; 327 328 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { 329 return; 330 } 331 332 add_history(arg); 333 334 RETURN_TRUE; 335} 336 337/* }}} */ 338/* {{{ proto bool readline_clear_history(void) 339 Clears the history */ 340PHP_FUNCTION(readline_clear_history) 341{ 342 if (zend_parse_parameters_none() == FAILURE) { 343 return; 344 } 345 346 clear_history(); 347 348 RETURN_TRUE; 349} 350 351/* }}} */ 352/* {{{ proto array readline_list_history(void) 353 Lists the history */ 354#ifndef HAVE_LIBEDIT 355PHP_FUNCTION(readline_list_history) 356{ 357 HIST_ENTRY **history; 358 359 if (zend_parse_parameters_none() == FAILURE) { 360 return; 361 } 362 363 history = history_list(); 364 365 array_init(return_value); 366 367 if (history) { 368 int i; 369 for (i = 0; history[i]; i++) { 370 add_next_index_string(return_value,history[i]->line,1); 371 } 372 } 373} 374#endif 375/* }}} */ 376/* {{{ proto bool readline_read_history([string filename]) 377 Reads the history */ 378PHP_FUNCTION(readline_read_history) 379{ 380 char *arg = NULL; 381 int arg_len; 382 383 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|p", &arg, &arg_len) == FAILURE) { 384 return; 385 } 386 387 if (php_check_open_basedir(arg TSRMLS_CC)) { 388 RETURN_FALSE; 389 } 390 391 /* XXX from & to NYI */ 392 if (read_history(arg)) { 393 RETURN_FALSE; 394 } else { 395 RETURN_TRUE; 396 } 397} 398 399/* }}} */ 400/* {{{ proto bool readline_write_history([string filename]) 401 Writes the history */ 402PHP_FUNCTION(readline_write_history) 403{ 404 char *arg = NULL; 405 int arg_len; 406 407 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|p", &arg, &arg_len) == FAILURE) { 408 return; 409 } 410 411 if (php_check_open_basedir(arg TSRMLS_CC)) { 412 RETURN_FALSE; 413 } 414 415 if (write_history(arg)) { 416 RETURN_FALSE; 417 } else { 418 RETURN_TRUE; 419 } 420} 421 422/* }}} */ 423/* {{{ proto bool readline_completion_function(string funcname) 424 Readline completion function? */ 425 426static char *_readline_command_generator(const char *text, int state) 427{ 428 HashTable *myht = Z_ARRVAL(_readline_array); 429 zval **entry; 430 431 if (!state) { 432 zend_hash_internal_pointer_reset(myht); 433 } 434 435 while (zend_hash_get_current_data(myht, (void **)&entry) == SUCCESS) { 436 zend_hash_move_forward(myht); 437 438 convert_to_string_ex(entry); 439 if (strncmp (Z_STRVAL_PP(entry), text, strlen(text)) == 0) { 440 return (strdup(Z_STRVAL_PP(entry))); 441 } 442 } 443 444 return NULL; 445} 446 447static zval *_readline_string_zval(const char *str) 448{ 449 zval *ret; 450 int len; 451 452 MAKE_STD_ZVAL(ret); 453 454 if (str) { 455 len = strlen(str); 456 ZVAL_STRINGL(ret, (char*)str, len, 1); 457 } else { 458 ZVAL_NULL(ret); 459 } 460 461 return ret; 462} 463 464static zval *_readline_long_zval(long l) 465{ 466 zval *ret; 467 MAKE_STD_ZVAL(ret); 468 469 Z_TYPE_P(ret) = IS_LONG; 470 Z_LVAL_P(ret) = l; 471 return ret; 472} 473 474static char **_readline_completion_cb(const char *text, int start, int end) 475{ 476 zval *params[3]; 477 int i; 478 char **matches = NULL; 479 TSRMLS_FETCH(); 480 481 params[0]=_readline_string_zval(text); 482 params[1]=_readline_long_zval(start); 483 params[2]=_readline_long_zval(end); 484 485 if (call_user_function(CG(function_table), NULL, _readline_completion, &_readline_array, 3, params TSRMLS_CC) == SUCCESS) { 486 if (Z_TYPE(_readline_array) == IS_ARRAY) { 487 if (zend_hash_num_elements(Z_ARRVAL(_readline_array))) { 488 matches = rl_completion_matches(text,_readline_command_generator); 489 } else { 490 matches = malloc(sizeof(char *) * 2); 491 if (!matches) { 492 return NULL; 493 } 494 matches[0] = strdup(""); 495 matches[1] = '\0'; 496 } 497 } 498 } 499 500 for (i = 0; i < 3; i++) { 501 zval_ptr_dtor(¶ms[i]); 502 } 503 zval_dtor(&_readline_array); 504 505 return matches; 506} 507 508PHP_FUNCTION(readline_completion_function) 509{ 510 zval *arg = NULL; 511 char *name = NULL; 512 513 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg)) { 514 RETURN_FALSE; 515 } 516 517 if (!zend_is_callable(arg, 0, &name TSRMLS_CC)) { 518 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name); 519 efree(name); 520 RETURN_FALSE; 521 } 522 efree(name); 523 524 if (_readline_completion) { 525 zval_dtor(_readline_completion); 526 FREE_ZVAL(_readline_completion); 527 } 528 529 MAKE_STD_ZVAL(_readline_completion); 530 *_readline_completion = *arg; 531 zval_copy_ctor(_readline_completion); 532 533 rl_attempted_completion_function = _readline_completion_cb; 534 if (rl_attempted_completion_function == NULL) { 535 efree(name); 536 RETURN_FALSE; 537 } 538 RETURN_TRUE; 539} 540 541/* }}} */ 542 543#if HAVE_RL_CALLBACK_READ_CHAR 544 545static void php_rl_callback_handler(char *the_line) 546{ 547 zval *params[1]; 548 zval dummy; 549 TSRMLS_FETCH(); 550 551 ZVAL_NULL(&dummy); 552 553 params[0] = _readline_string_zval(the_line); 554 555 call_user_function(CG(function_table), NULL, _prepped_callback, &dummy, 1, params TSRMLS_CC); 556 557 zval_ptr_dtor(¶ms[0]); 558 zval_dtor(&dummy); 559} 560 561/* {{{ proto void readline_callback_handler_install(string prompt, mixed callback) 562 Initializes the readline callback interface and terminal, prints the prompt and returns immediately */ 563PHP_FUNCTION(readline_callback_handler_install) 564{ 565 zval *callback; 566 char *name = NULL; 567 char *prompt; 568 int prompt_len; 569 570 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &prompt, &prompt_len, &callback)) { 571 return; 572 } 573 574 if (!zend_is_callable(callback, 0, &name TSRMLS_CC)) { 575 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not callable", name); 576 efree(name); 577 RETURN_FALSE; 578 } 579 efree(name); 580 581 if (_prepped_callback) { 582 rl_callback_handler_remove(); 583 zval_dtor(_prepped_callback); 584 FREE_ZVAL(_prepped_callback); 585 } 586 587 ALLOC_ZVAL(_prepped_callback); 588 MAKE_COPY_ZVAL(&callback, _prepped_callback); 589 590 rl_callback_handler_install(prompt, php_rl_callback_handler); 591 592 RETURN_TRUE; 593} 594/* }}} */ 595 596/* {{{ proto void readline_callback_read_char() 597 Informs the readline callback interface that a character is ready for input */ 598PHP_FUNCTION(readline_callback_read_char) 599{ 600 if (_prepped_callback) { 601 rl_callback_read_char(); 602 } 603} 604/* }}} */ 605 606/* {{{ proto bool readline_callback_handler_remove() 607 Removes a previously installed callback handler and restores terminal settings */ 608PHP_FUNCTION(readline_callback_handler_remove) 609{ 610 if (_prepped_callback) { 611 rl_callback_handler_remove(); 612 zval_dtor(_prepped_callback); 613 FREE_ZVAL(_prepped_callback); 614 _prepped_callback = 0; 615 RETURN_TRUE; 616 } 617 RETURN_FALSE; 618} 619/* }}} */ 620 621/* {{{ proto void readline_redisplay(void) 622 Ask readline to redraw the display */ 623PHP_FUNCTION(readline_redisplay) 624{ 625 rl_redisplay(); 626} 627/* }}} */ 628 629#endif 630 631#if HAVE_RL_ON_NEW_LINE 632/* {{{ proto void readline_on_new_line(void) 633 Inform readline that the cursor has moved to a new line */ 634PHP_FUNCTION(readline_on_new_line) 635{ 636 rl_on_new_line(); 637} 638/* }}} */ 639 640#endif 641 642 643#endif /* HAVE_LIBREADLINE */ 644 645/* 646 * Local variables: 647 * tab-width: 4 648 * c-basic-offset: 4 649 * End: 650 */ 651