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(¶ms[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(¶ms[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