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