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