xref: /PHP-5.3/ext/readline/readline.c (revision a2045ff3)
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 
43 PHP_FUNCTION(readline);
44 PHP_FUNCTION(readline_add_history);
45 PHP_FUNCTION(readline_info);
46 PHP_FUNCTION(readline_clear_history);
47 #ifndef HAVE_LIBEDIT
48 PHP_FUNCTION(readline_list_history);
49 #endif
50 PHP_FUNCTION(readline_read_history);
51 PHP_FUNCTION(readline_write_history);
52 PHP_FUNCTION(readline_completion_function);
53 
54 #if HAVE_RL_CALLBACK_READ_CHAR
55 PHP_FUNCTION(readline_callback_handler_install);
56 PHP_FUNCTION(readline_callback_read_char);
57 PHP_FUNCTION(readline_callback_handler_remove);
58 PHP_FUNCTION(readline_redisplay);
59 PHP_FUNCTION(readline_on_new_line);
60 
61 static zval *_prepped_callback = NULL;
62 
63 #endif
64 
65 static zval *_readline_completion = NULL;
66 static zval _readline_array;
67 
68 PHP_MINIT_FUNCTION(readline);
69 PHP_RSHUTDOWN_FUNCTION(readline);
70 
71 /* }}} */
72 
73 /* {{{ arginfo */
74 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline, 0, 0, 0)
75 	ZEND_ARG_INFO(0, prompt)
76 ZEND_END_ARG_INFO()
77 
78 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_info, 0, 0, 0)
79 	ZEND_ARG_INFO(0, varname)
80 	ZEND_ARG_INFO(0, newvalue)
81 ZEND_END_ARG_INFO()
82 
83 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_add_history, 0, 0, 1)
84 	ZEND_ARG_INFO(0, prompt)
85 ZEND_END_ARG_INFO()
86 
87 ZEND_BEGIN_ARG_INFO(arginfo_readline_clear_history, 0)
88 ZEND_END_ARG_INFO()
89 
90 #ifndef HAVE_LIBEDIT
91 ZEND_BEGIN_ARG_INFO(arginfo_readline_list_history, 0)
92 ZEND_END_ARG_INFO()
93 #endif
94 
95 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_read_history, 0, 0, 0)
96 	ZEND_ARG_INFO(0, filename)
97 ZEND_END_ARG_INFO()
98 
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_write_history, 0, 0, 0)
100 	ZEND_ARG_INFO(0, filename)
101 ZEND_END_ARG_INFO()
102 
103 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_completion_function, 0, 0, 1)
104 	ZEND_ARG_INFO(0, funcname)
105 ZEND_END_ARG_INFO()
106 
107 #if HAVE_RL_CALLBACK_READ_CHAR
108 ZEND_BEGIN_ARG_INFO_EX(arginfo_readline_callback_handler_install, 0, 0, 2)
109 	ZEND_ARG_INFO(0, prompt)
110 	ZEND_ARG_INFO(0, callback)
111 ZEND_END_ARG_INFO()
112 
113 ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_read_char, 0)
114 ZEND_END_ARG_INFO()
115 
116 ZEND_BEGIN_ARG_INFO(arginfo_readline_callback_handler_remove, 0)
117 ZEND_END_ARG_INFO()
118 
119 ZEND_BEGIN_ARG_INFO(arginfo_readline_redisplay, 0)
120 ZEND_END_ARG_INFO()
121 
122 ZEND_BEGIN_ARG_INFO(arginfo_readline_on_new_line, 0)
123 ZEND_END_ARG_INFO()
124 #endif
125 /* }}} */
126 
127 /* {{{ module stuff */
128 static 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 
149 zend_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
163 ZEND_GET_MODULE(readline)
164 #endif
165 
PHP_MINIT_FUNCTION(readline)166 PHP_MINIT_FUNCTION(readline)
167 {
168     	using_history();
169     	return SUCCESS;
170 }
171 
PHP_RSHUTDOWN_FUNCTION(readline)172 PHP_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 */
PHP_FUNCTION(readline)193 PHP_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. */
PHP_FUNCTION(readline_info)219 PHP_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 */
PHP_FUNCTION(readline_add_history)308 PHP_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 */
PHP_FUNCTION(readline_clear_history)325 PHP_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
PHP_FUNCTION(readline_list_history)340 PHP_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 */
PHP_FUNCTION(readline_read_history)363 PHP_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 */
PHP_FUNCTION(readline_write_history)387 PHP_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 
_readline_command_generator(const char * text,int state)411 static 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 
_readline_string_zval(const char * str)432 static 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 
_readline_long_zval(long l)449 static 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 
_readline_completion_cb(const char * text,int start,int end)459 static 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(&params[i]);
487 	}
488 	zval_dtor(&_readline_array);
489 
490 	return matches;
491 }
492 
PHP_FUNCTION(readline_completion_function)493 PHP_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 
php_rl_callback_handler(char * the_line)530 static 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(&params[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 */
PHP_FUNCTION(readline_callback_handler_install)548 PHP_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 */
PHP_FUNCTION(readline_callback_read_char)583 PHP_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 */
PHP_FUNCTION(readline_callback_handler_remove)593 PHP_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 */
PHP_FUNCTION(readline_redisplay)608 PHP_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 */
PHP_FUNCTION(readline_on_new_line)616 PHP_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