xref: /PHP-7.4/ext/readline/readline.c (revision 9450893d)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 /* {{{ includes & prototypes */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "php.h"
26 #include "php_readline.h"
27 #include "readline_cli.h"
28 
29 #if HAVE_LIBREADLINE || HAVE_LIBEDIT
30 
31 #ifndef HAVE_RL_COMPLETION_MATCHES
32 #define rl_completion_matches completion_matches
33 #endif
34 
35 #ifdef HAVE_LIBEDIT
36 #include <editline/readline.h>
37 #else
38 #include <readline/readline.h>
39 #include <readline/history.h>
40 #endif
41 
42 PHP_FUNCTION(readline);
43 PHP_FUNCTION(readline_add_history);
44 PHP_FUNCTION(readline_info);
45 PHP_FUNCTION(readline_clear_history);
46 #ifdef HAVE_HISTORY_LIST
47 PHP_FUNCTION(readline_list_history);
48 #endif
49 PHP_FUNCTION(readline_read_history);
50 PHP_FUNCTION(readline_write_history);
51 PHP_FUNCTION(readline_completion_function);
52 
53 #if HAVE_RL_CALLBACK_READ_CHAR
54 PHP_FUNCTION(readline_callback_handler_install);
55 PHP_FUNCTION(readline_callback_read_char);
56 PHP_FUNCTION(readline_callback_handler_remove);
57 PHP_FUNCTION(readline_redisplay);
58 PHP_FUNCTION(readline_on_new_line);
59 
60 static zval _prepped_callback;
61 
62 #endif
63 
64 static zval _readline_completion;
65 static zval _readline_array;
66 
67 PHP_MINIT_FUNCTION(readline);
68 PHP_MSHUTDOWN_FUNCTION(readline);
69 PHP_RSHUTDOWN_FUNCTION(readline);
70 PHP_MINFO_FUNCTION(readline);
71 
72 /* }}} */
73 
74 /* {{{ arginfo */
75 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline, 0, 0, 0)
76 	ZEND_ARG_INFO(0, prompt)
77 ZEND_END_ARG_INFO()
78 
79 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_info, 0, 0, 0)
80 	ZEND_ARG_INFO(0, varname)
81 	ZEND_ARG_INFO(0, newvalue)
82 ZEND_END_ARG_INFO()
83 
84 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_add_history, 0, 0, 1)
85 	ZEND_ARG_INFO(0, prompt)
86 ZEND_END_ARG_INFO()
87 
88 ZEND_BEGIN_ARG_INFO(arginfo_readline_clear_history, 0)
89 ZEND_END_ARG_INFO()
90 
91 #ifdef HAVE_HISTORY_LIST
92 ZEND_BEGIN_ARG_INFO(arginfo_readline_list_history, 0)
93 ZEND_END_ARG_INFO()
94 #endif
95 
96 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_read_history, 0, 0, 0)
97 	ZEND_ARG_INFO(0, filename)
98 ZEND_END_ARG_INFO()
99 
100 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_write_history, 0, 0, 0)
101 	ZEND_ARG_INFO(0, filename)
102 ZEND_END_ARG_INFO()
103 
104 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_completion_function, 0, 0, 1)
105 	ZEND_ARG_INFO(0, funcname)
106 ZEND_END_ARG_INFO()
107 
108 #if HAVE_RL_CALLBACK_READ_CHAR
109 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_callback_handler_install, 0, 0, 2)
110 	ZEND_ARG_INFO(0, prompt)
111 	ZEND_ARG_INFO(0, callback)
112 ZEND_END_ARG_INFO()
113 
114 ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_read_char, 0)
115 ZEND_END_ARG_INFO()
116 
117 ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_handler_remove, 0)
118 ZEND_END_ARG_INFO()
119 
120 ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0)
121 ZEND_END_ARG_INFO()
122 
123 #if HAVE_RL_ON_NEW_LINE
124 ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0)
125 ZEND_END_ARG_INFO()
126 #endif
127 #endif
128 /* }}} */
129 
130 /* {{{ module stuff */
131 static 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 #ifdef HAVE_HISTORY_LIST
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 
154 zend_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_READLINE_VERSION,
164 	STANDARD_MODULE_PROPERTIES
165 };
166 
167 #ifdef COMPILE_DL_READLINE
168 ZEND_GET_MODULE(readline)
169 #endif
170 
PHP_MINIT_FUNCTION(readline)171 PHP_MINIT_FUNCTION(readline)
172 {
173 #if HAVE_LIBREADLINE
174 		/* libedit don't need this call which set the tty in cooked mode */
175 	using_history();
176 #endif
177 	ZVAL_UNDEF(&_readline_completion);
178 #if HAVE_RL_CALLBACK_READ_CHAR
179 	ZVAL_UNDEF(&_prepped_callback);
180 #endif
181    	return PHP_MINIT(cli_readline)(INIT_FUNC_ARGS_PASSTHRU);
182 }
183 
PHP_MSHUTDOWN_FUNCTION(readline)184 PHP_MSHUTDOWN_FUNCTION(readline)
185 {
186 	return PHP_MSHUTDOWN(cli_readline)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
187 }
188 
PHP_RSHUTDOWN_FUNCTION(readline)189 PHP_RSHUTDOWN_FUNCTION(readline)
190 {
191 	zval_ptr_dtor(&_readline_completion);
192 	ZVAL_UNDEF(&_readline_completion);
193 #if HAVE_RL_CALLBACK_READ_CHAR
194 	if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
195 		rl_callback_handler_remove();
196 		zval_ptr_dtor(&_prepped_callback);
197 		ZVAL_UNDEF(&_prepped_callback);
198 	}
199 #endif
200 
201 	return SUCCESS;
202 }
203 
PHP_MINFO_FUNCTION(readline)204 PHP_MINFO_FUNCTION(readline)
205 {
206 	PHP_MINFO(cli_readline)(ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU);
207 }
208 
209 /* }}} */
210 
211 /* {{{ proto string readline([string prompt])
212    Reads a line */
PHP_FUNCTION(readline)213 PHP_FUNCTION(readline)
214 {
215 	char *prompt = NULL;
216 	size_t prompt_len;
217 	char *result;
218 
219 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &prompt, &prompt_len)) {
220 		RETURN_FALSE;
221 	}
222 
223 	result = readline(prompt);
224 
225 	if (! result) {
226 		RETURN_FALSE;
227 	} else {
228 		RETVAL_STRING(result);
229 		free(result);
230 	}
231 }
232 
233 /* }}} */
234 
235 #define SAFE_STRING(s) ((s)?(char*)(s):"")
236 
237 /* {{{ proto mixed readline_info([string varname [, string newvalue]])
238    Gets/sets various internal readline variables. */
PHP_FUNCTION(readline_info)239 PHP_FUNCTION(readline_info)
240 {
241 	char *what = NULL;
242 	zval *value = NULL;
243 	size_t what_len, oldval;
244 	char *oldstr;
245 
246 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sz", &what, &what_len, &value) == FAILURE) {
247 		return;
248 	}
249 
250 	if (!what) {
251 		array_init(return_value);
252 		add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer));
253 		add_assoc_long(return_value,"point",rl_point);
254 #ifndef PHP_WIN32
255 		add_assoc_long(return_value,"end",rl_end);
256 #endif
257 #ifdef HAVE_LIBREADLINE
258 		add_assoc_long(return_value,"mark",rl_mark);
259 		add_assoc_long(return_value,"done",rl_done);
260 		add_assoc_long(return_value,"pending_input",rl_pending_input);
261 		add_assoc_string(return_value,"prompt",SAFE_STRING(rl_prompt));
262 		add_assoc_string(return_value,"terminal_name",(char *)SAFE_STRING(rl_terminal_name));
263 		add_assoc_str(return_value, "completion_append_character",
264 			rl_completion_append_character == 0
265 				? ZSTR_EMPTY_ALLOC()
266 				: ZSTR_CHAR(rl_completion_append_character));
267 		add_assoc_bool(return_value,"completion_suppress_append",rl_completion_suppress_append);
268 #endif
269 #if HAVE_ERASE_EMPTY_LINE
270 		add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line);
271 #endif
272 #ifndef PHP_WIN32
273 		add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version));
274 #endif
275 		add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name));
276 		add_assoc_long(return_value,"attempted_completion_over",rl_attempted_completion_over);
277 	} else {
278 		if (!strcasecmp(what,"line_buffer")) {
279 			oldstr = rl_line_buffer;
280 			if (value) {
281 				/* XXX if (rl_line_buffer) free(rl_line_buffer); */
282 				if (!try_convert_to_string(value)) {
283 					return;
284 				}
285 				rl_line_buffer = strdup(Z_STRVAL_P(value));
286 			}
287 			RETVAL_STRING(SAFE_STRING(oldstr));
288 		} else if (!strcasecmp(what, "point")) {
289 			RETVAL_LONG(rl_point);
290 #ifndef PHP_WIN32
291 		} else if (!strcasecmp(what, "end")) {
292 			RETVAL_LONG(rl_end);
293 #endif
294 #ifdef HAVE_LIBREADLINE
295 		} else if (!strcasecmp(what, "mark")) {
296 			RETVAL_LONG(rl_mark);
297 		} else if (!strcasecmp(what, "done")) {
298 			oldval = rl_done;
299 			if (value) {
300 				convert_to_long_ex(value);
301 				rl_done = Z_LVAL_P(value);
302 			}
303 			RETVAL_LONG(oldval);
304 		} else if (!strcasecmp(what, "pending_input")) {
305 			oldval = rl_pending_input;
306 			if (value) {
307 				if (!try_convert_to_string(value)) {
308 					return;
309 				}
310 				rl_pending_input = Z_STRVAL_P(value)[0];
311 			}
312 			RETVAL_LONG(oldval);
313 		} else if (!strcasecmp(what, "prompt")) {
314 			RETVAL_STRING(SAFE_STRING(rl_prompt));
315 		} else if (!strcasecmp(what, "terminal_name")) {
316 			RETVAL_STRING((char *)SAFE_STRING(rl_terminal_name));
317 		} else if (!strcasecmp(what, "completion_suppress_append")) {
318 			oldval = rl_completion_suppress_append;
319 			if (value) {
320 				rl_completion_suppress_append = zend_is_true(value);
321 			}
322 			RETVAL_BOOL(oldval);
323 		} else if (!strcasecmp(what, "completion_append_character")) {
324 			oldval = rl_completion_append_character;
325 			if (value) {
326 				if (!try_convert_to_string(value)) {
327 					return;
328 				}
329 				rl_completion_append_character = (int)Z_STRVAL_P(value)[0];
330 			}
331 			RETVAL_INTERNED_STR(
332 				oldval == 0 ? ZSTR_EMPTY_ALLOC() : ZSTR_CHAR(oldval));
333 #endif
334 #if HAVE_ERASE_EMPTY_LINE
335 		} else if (!strcasecmp(what, "erase_empty_line")) {
336 			oldval = rl_erase_empty_line;
337 			if (value) {
338 				convert_to_long_ex(value);
339 				rl_erase_empty_line = Z_LVAL_P(value);
340 			}
341 			RETVAL_LONG(oldval);
342 #endif
343 #ifndef PHP_WIN32
344 		} else if (!strcasecmp(what,"library_version")) {
345 			RETVAL_STRING((char *)SAFE_STRING(rl_library_version));
346 #endif
347 		} else if (!strcasecmp(what, "readline_name")) {
348 			oldstr = (char*)rl_readline_name;
349 			if (value) {
350 				/* XXX if (rl_readline_name) free(rl_readline_name); */
351 				if (!try_convert_to_string(value)) {
352 					return;
353 				}
354 				rl_readline_name = strdup(Z_STRVAL_P(value));
355 			}
356 			RETVAL_STRING(SAFE_STRING(oldstr));
357 		} else if (!strcasecmp(what, "attempted_completion_over")) {
358 			oldval = rl_attempted_completion_over;
359 			if (value) {
360 				convert_to_long_ex(value);
361 				rl_attempted_completion_over = Z_LVAL_P(value);
362 			}
363 			RETVAL_LONG(oldval);
364 		}
365 	}
366 }
367 
368 /* }}} */
369 /* {{{ proto bool readline_add_history(string prompt)
370    Adds a line to the history */
PHP_FUNCTION(readline_add_history)371 PHP_FUNCTION(readline_add_history)
372 {
373 	char *arg;
374 	size_t arg_len;
375 
376 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
377 		return;
378 	}
379 
380 	add_history(arg);
381 
382 	RETURN_TRUE;
383 }
384 
385 /* }}} */
386 /* {{{ proto bool readline_clear_history(void)
387    Clears the history */
PHP_FUNCTION(readline_clear_history)388 PHP_FUNCTION(readline_clear_history)
389 {
390 	if (zend_parse_parameters_none() == FAILURE) {
391 		return;
392 	}
393 
394 #if HAVE_LIBEDIT
395 	/* clear_history is the only function where rl_initialize
396 	   is not call to ensure correct allocation */
397 	using_history();
398 #endif
399 	clear_history();
400 
401 	RETURN_TRUE;
402 }
403 
404 /* }}} */
405 
406 #ifdef HAVE_HISTORY_LIST
407 /* {{{ proto array readline_list_history(void)
408    Lists the history */
PHP_FUNCTION(readline_list_history)409 PHP_FUNCTION(readline_list_history)
410 {
411 	HIST_ENTRY **history;
412 
413 	if (zend_parse_parameters_none() == FAILURE) {
414 		return;
415 	}
416 
417 	array_init(return_value);
418 
419 #if defined(HAVE_LIBEDIT) && defined(PHP_WIN32) /* Winedit on Windows */
420 	history = history_list();
421 
422 	if (history) {
423 		int i, n = history_length();
424 		for (i = 0; i < n; i++) {
425 				add_next_index_string(return_value, history[i]->line);
426 		}
427 	}
428 
429 #elif defined(HAVE_LIBEDIT) /* libedit */
430     {
431 		HISTORY_STATE *hs;
432 		int i;
433 
434 		using_history();
435 		hs = history_get_history_state();
436 		if (hs && hs->length) {
437 			history = history_list();
438 			if (history) {
439 				for (i = 0; i < hs->length; i++) {
440 					add_next_index_string(return_value, history[i]->line);
441 				}
442 			}
443 		}
444 		free(hs);
445     }
446 
447 #else /* readline */
448 	history = history_list();
449 
450 	if (history) {
451 		int i;
452 		for (i = 0; history[i]; i++) {
453 			add_next_index_string(return_value, history[i]->line);
454 		}
455 	}
456 #endif
457 }
458 /* }}} */
459 #endif
460 
461 /* {{{ proto bool readline_read_history([string filename])
462    Reads the history */
PHP_FUNCTION(readline_read_history)463 PHP_FUNCTION(readline_read_history)
464 {
465 	char *arg = NULL;
466 	size_t arg_len;
467 
468 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p", &arg, &arg_len) == FAILURE) {
469 		return;
470 	}
471 
472 	if (arg && php_check_open_basedir(arg)) {
473 		RETURN_FALSE;
474 	}
475 
476 	/* XXX from & to NYI */
477 	if (read_history(arg)) {
478 		/* If filename is NULL, then read from `~/.history' */
479 		RETURN_FALSE;
480 	} else {
481 		RETURN_TRUE;
482 	}
483 }
484 
485 /* }}} */
486 /* {{{ proto bool readline_write_history([string filename])
487    Writes the history */
PHP_FUNCTION(readline_write_history)488 PHP_FUNCTION(readline_write_history)
489 {
490 	char *arg = NULL;
491 	size_t arg_len;
492 
493 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p", &arg, &arg_len) == FAILURE) {
494 		return;
495 	}
496 
497 	if (arg && php_check_open_basedir(arg)) {
498 		RETURN_FALSE;
499 	}
500 
501 	if (write_history(arg)) {
502 		RETURN_FALSE;
503 	} else {
504 		RETURN_TRUE;
505 	}
506 }
507 
508 /* }}} */
509 /* {{{ proto bool readline_completion_function(string funcname)
510    Readline completion function? */
511 
_readline_command_generator(const char * text,int state)512 static char *_readline_command_generator(const char *text, int state)
513 {
514 	HashTable  *myht = Z_ARRVAL(_readline_array);
515 	zval *entry;
516 
517 	if (!state) {
518 		zend_hash_internal_pointer_reset(myht);
519 	}
520 
521 	while ((entry = zend_hash_get_current_data(myht)) != NULL) {
522 		zend_hash_move_forward(myht);
523 
524 		convert_to_string_ex(entry);
525 		if (strncmp (Z_STRVAL_P(entry), text, strlen(text)) == 0) {
526 			return (strdup(Z_STRVAL_P(entry)));
527 		}
528 	}
529 
530 	return NULL;
531 }
532 
_readline_string_zval(zval * ret,const char * str)533 static void _readline_string_zval(zval *ret, const char *str)
534 {
535 	if (str) {
536 		ZVAL_STRING(ret, (char*)str);
537 	} else {
538 		ZVAL_NULL(ret);
539 	}
540 }
541 
_readline_long_zval(zval * ret,long l)542 static void _readline_long_zval(zval *ret, long l)
543 {
544 	ZVAL_LONG(ret, l);
545 }
546 
_readline_completion_cb(const char * text,int start,int end)547 static char **_readline_completion_cb(const char *text, int start, int end)
548 {
549 	zval params[3];
550 	char **matches = NULL;
551 
552 	_readline_string_zval(&params[0], text);
553 	_readline_long_zval(&params[1], start);
554 	_readline_long_zval(&params[2], end);
555 
556 	if (call_user_function(NULL, NULL, &_readline_completion, &_readline_array, 3, params) == SUCCESS) {
557 		if (Z_TYPE(_readline_array) == IS_ARRAY) {
558 			SEPARATE_ARRAY(&_readline_array);
559 			if (zend_hash_num_elements(Z_ARRVAL(_readline_array))) {
560 				matches = rl_completion_matches(text,_readline_command_generator);
561 			} else {
562 				matches = malloc(sizeof(char *) * 2);
563 				if (!matches) {
564 					return NULL;
565 				}
566 				matches[0] = strdup("");
567 				matches[1] = NULL;
568 			}
569 		}
570 	}
571 
572 	zval_ptr_dtor(&params[0]);
573 	zval_ptr_dtor(&_readline_array);
574 
575 	return matches;
576 }
577 
PHP_FUNCTION(readline_completion_function)578 PHP_FUNCTION(readline_completion_function)
579 {
580 	zval *arg;
581 
582 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg)) {
583 		RETURN_FALSE;
584 	}
585 
586 	if (!zend_is_callable(arg, 0, NULL)) {
587 		zend_string *name = zend_get_callable_name(arg);
588 		php_error_docref(NULL, E_WARNING, "%s is not callable", ZSTR_VAL(name));
589 		zend_string_release_ex(name, 0);
590 		RETURN_FALSE;
591 	}
592 
593 	zval_ptr_dtor(&_readline_completion);
594 	ZVAL_COPY(&_readline_completion, arg);
595 
596 	rl_attempted_completion_function = _readline_completion_cb;
597 	if (rl_attempted_completion_function == NULL) {
598 		RETURN_FALSE;
599 	}
600 	RETURN_TRUE;
601 }
602 
603 /* }}} */
604 
605 #if HAVE_RL_CALLBACK_READ_CHAR
606 
php_rl_callback_handler(char * the_line)607 static void php_rl_callback_handler(char *the_line)
608 {
609 	zval params[1];
610 	zval dummy;
611 
612 	ZVAL_NULL(&dummy);
613 
614 	_readline_string_zval(&params[0], the_line);
615 
616 	call_user_function(NULL, NULL, &_prepped_callback, &dummy, 1, params);
617 
618 	zval_ptr_dtor(&params[0]);
619 	zval_ptr_dtor(&dummy);
620 }
621 
622 /* {{{ proto void readline_callback_handler_install(string prompt, mixed callback)
623    Initializes the readline callback interface and terminal, prints the prompt and returns immediately */
PHP_FUNCTION(readline_callback_handler_install)624 PHP_FUNCTION(readline_callback_handler_install)
625 {
626 	zval *callback;
627 	char *prompt;
628 	size_t prompt_len;
629 
630 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &prompt, &prompt_len, &callback)) {
631 		return;
632 	}
633 
634 	if (!zend_is_callable(callback, 0, NULL)) {
635 		zend_string *name = zend_get_callable_name(callback);
636 		php_error_docref(NULL, E_WARNING, "%s is not callable", ZSTR_VAL(name));
637 		zend_string_release_ex(name, 0);
638 		RETURN_FALSE;
639 	}
640 
641 	if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
642 		rl_callback_handler_remove();
643 		zval_ptr_dtor(&_prepped_callback);
644 	}
645 
646 	ZVAL_COPY(&_prepped_callback, callback);
647 
648 	rl_callback_handler_install(prompt, php_rl_callback_handler);
649 
650 	RETURN_TRUE;
651 }
652 /* }}} */
653 
654 /* {{{ proto void readline_callback_read_char()
655    Informs the readline callback interface that a character is ready for input */
PHP_FUNCTION(readline_callback_read_char)656 PHP_FUNCTION(readline_callback_read_char)
657 {
658 	if (zend_parse_parameters_none() == FAILURE) {
659 		return;
660 	}
661 
662 	if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
663 		rl_callback_read_char();
664 	}
665 }
666 /* }}} */
667 
668 /* {{{ proto bool readline_callback_handler_remove()
669    Removes a previously installed callback handler and restores terminal settings */
PHP_FUNCTION(readline_callback_handler_remove)670 PHP_FUNCTION(readline_callback_handler_remove)
671 {
672 	if (zend_parse_parameters_none() == FAILURE) {
673 		return;
674 	}
675 
676 	if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
677 		rl_callback_handler_remove();
678 		zval_ptr_dtor(&_prepped_callback);
679 		ZVAL_UNDEF(&_prepped_callback);
680 		RETURN_TRUE;
681 	}
682 	RETURN_FALSE;
683 }
684 /* }}} */
685 
686 /* {{{ proto void readline_redisplay(void)
687    Ask readline to redraw the display */
PHP_FUNCTION(readline_redisplay)688 PHP_FUNCTION(readline_redisplay)
689 {
690 	if (zend_parse_parameters_none() == FAILURE) {
691 		return;
692 	}
693 
694 #if HAVE_LIBEDIT
695 	/* seems libedit doesn't take care of rl_initialize in rl_redisplay
696 	 * see bug #72538 */
697 	using_history();
698 #endif
699 	rl_redisplay();
700 }
701 /* }}} */
702 
703 #endif
704 
705 #if HAVE_RL_ON_NEW_LINE
706 /* {{{ proto void readline_on_new_line(void)
707    Inform readline that the cursor has moved to a new line */
PHP_FUNCTION(readline_on_new_line)708 PHP_FUNCTION(readline_on_new_line)
709 {
710 	if (zend_parse_parameters_none() == FAILURE) {
711 		return;
712 	}
713 
714 	rl_on_new_line();
715 }
716 /* }}} */
717 
718 #endif
719 
720 
721 #endif /* HAVE_LIBREADLINE */
722