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