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