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