1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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: Marcus Boerger <helly@php.net> |
16 | Johannes Schlueter <johannes@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "php.h"
25
26 #ifndef HAVE_RL_COMPLETION_MATCHES
27 #define rl_completion_matches completion_matches
28 #endif
29
30 #include "php_globals.h"
31 #include "php_variables.h"
32 #include "zend_hash.h"
33 #include "zend_modules.h"
34
35 #include "SAPI.h"
36
37 #if HAVE_SETLOCALE
38 #include <locale.h>
39 #endif
40 #include "zend.h"
41 #include "zend_extensions.h"
42 #include "php_ini.h"
43 #include "php_globals.h"
44 #include "php_main.h"
45 #include "fopen_wrappers.h"
46 #include "ext/standard/php_standard.h"
47 #include "zend_smart_str.h"
48
49 #ifdef __riscos__
50 #include <unixlib/local.h>
51 #endif
52
53 #if HAVE_LIBEDIT
54 #include <editline/readline.h>
55 #else
56 #include <readline/readline.h>
57 #include <readline/history.h>
58 #endif
59
60 #include "zend_compile.h"
61 #include "zend_execute.h"
62 #include "zend_highlight.h"
63 #include "zend_exceptions.h"
64
65 #include "sapi/cli/cli.h"
66 #include "readline_cli.h"
67
68 #if defined(COMPILE_DL_READLINE) && !defined(PHP_WIN32)
69 #include <dlfcn.h>
70 #endif
71
72 #ifndef RTLD_DEFAULT
73 #define RTLD_DEFAULT NULL
74 #endif
75
76 #define DEFAULT_PROMPT "\\b \\> "
77
78 ZEND_DECLARE_MODULE_GLOBALS(cli_readline);
79
80 static char php_last_char = '\0';
81 static FILE *pager_pipe = NULL;
82
readline_shell_write(const char * str,size_t str_length)83 static size_t readline_shell_write(const char *str, size_t str_length) /* {{{ */
84 {
85 if (CLIR_G(prompt_str)) {
86 smart_str_appendl(CLIR_G(prompt_str), str, str_length);
87 return str_length;
88 }
89
90 if (CLIR_G(pager) && *CLIR_G(pager) && !pager_pipe) {
91 pager_pipe = VCWD_POPEN(CLIR_G(pager), "w");
92 }
93 if (pager_pipe) {
94 return fwrite(str, 1, MIN(str_length, 16384), pager_pipe);
95 }
96
97 return -1;
98 }
99 /* }}} */
100
readline_shell_ub_write(const char * str,size_t str_length)101 static size_t readline_shell_ub_write(const char *str, size_t str_length) /* {{{ */
102 {
103 /* We just store the last char here and then pass back to the
104 caller (sapi_cli_single_write in sapi/cli) which will actually
105 write due to -1 return code */
106 php_last_char = str[str_length-1];
107
108 return (size_t) -1;
109 }
110 /* }}} */
111
cli_readline_init_globals(zend_cli_readline_globals * rg)112 static void cli_readline_init_globals(zend_cli_readline_globals *rg)
113 {
114 rg->pager = NULL;
115 rg->prompt = NULL;
116 rg->prompt_str = NULL;
117 }
118
119 PHP_INI_BEGIN()
120 STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals)
121 STD_PHP_INI_ENTRY("cli.prompt", DEFAULT_PROMPT, PHP_INI_ALL, OnUpdateString, prompt, zend_cli_readline_globals, cli_readline_globals)
122 PHP_INI_END()
123
124
125
126 typedef enum {
127 body,
128 sstring,
129 dstring,
130 sstring_esc,
131 dstring_esc,
132 comment_line,
133 comment_block,
134 heredoc_start,
135 heredoc,
136 outside,
137 } php_code_type;
138
cli_get_prompt(char * block,char prompt)139 static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */
140 {
141 smart_str retval = {0};
142 char *prompt_spec = CLIR_G(prompt) ? CLIR_G(prompt) : DEFAULT_PROMPT;
143
144 do {
145 if (*prompt_spec == '\\') {
146 switch (prompt_spec[1]) {
147 case '\\':
148 smart_str_appendc(&retval, '\\');
149 prompt_spec++;
150 break;
151 case 'n':
152 smart_str_appendc(&retval, '\n');
153 prompt_spec++;
154 break;
155 case 't':
156 smart_str_appendc(&retval, '\t');
157 prompt_spec++;
158 break;
159 case 'e':
160 smart_str_appendc(&retval, '\033');
161 prompt_spec++;
162 break;
163
164
165 case 'v':
166 smart_str_appends(&retval, PHP_VERSION);
167 prompt_spec++;
168 break;
169 case 'b':
170 smart_str_appends(&retval, block);
171 prompt_spec++;
172 break;
173 case '>':
174 smart_str_appendc(&retval, prompt);
175 prompt_spec++;
176 break;
177 case '`':
178 smart_str_appendc(&retval, '`');
179 prompt_spec++;
180 break;
181 default:
182 smart_str_appendc(&retval, '\\');
183 break;
184 }
185 } else if (*prompt_spec == '`') {
186 char *prompt_end = strstr(prompt_spec + 1, "`");
187 char *code;
188
189 if (prompt_end) {
190 code = estrndup(prompt_spec + 1, prompt_end - prompt_spec - 1);
191
192 CLIR_G(prompt_str) = &retval;
193 zend_try {
194 zend_eval_stringl(code, prompt_end - prompt_spec - 1, NULL, "php prompt code");
195 } zend_end_try();
196 CLIR_G(prompt_str) = NULL;
197 efree(code);
198 prompt_spec = prompt_end;
199 }
200 } else {
201 smart_str_appendc(&retval, *prompt_spec);
202 }
203 } while (++prompt_spec && *prompt_spec);
204 smart_str_0(&retval);
205 return retval.s;
206 }
207 /* }}} */
208
cli_is_valid_code(char * code,size_t len,zend_string ** prompt)209 static int cli_is_valid_code(char *code, size_t len, zend_string **prompt) /* {{{ */
210 {
211 int valid_end = 1, last_valid_end;
212 int brackets_count = 0;
213 int brace_count = 0;
214 size_t i;
215 php_code_type code_type = body;
216 char *heredoc_tag;
217 size_t heredoc_len;
218
219 for (i = 0; i < len; ++i) {
220 switch(code_type) {
221 default:
222 switch(code[i]) {
223 case '{':
224 brackets_count++;
225 valid_end = 0;
226 break;
227 case '}':
228 if (brackets_count > 0) {
229 brackets_count--;
230 }
231 valid_end = brackets_count ? 0 : 1;
232 break;
233 case '(':
234 brace_count++;
235 valid_end = 0;
236 break;
237 case ')':
238 if (brace_count > 0) {
239 brace_count--;
240 }
241 valid_end = 0;
242 break;
243 case ';':
244 valid_end = brace_count == 0 && brackets_count == 0;
245 break;
246 case ' ':
247 case '\r':
248 case '\n':
249 case '\t':
250 break;
251 case '\'':
252 code_type = sstring;
253 break;
254 case '"':
255 code_type = dstring;
256 break;
257 case '#':
258 code_type = comment_line;
259 break;
260 case '/':
261 if (code[i+1] == '/') {
262 i++;
263 code_type = comment_line;
264 break;
265 }
266 if (code[i+1] == '*') {
267 last_valid_end = valid_end;
268 valid_end = 0;
269 code_type = comment_block;
270 i++;
271 break;
272 }
273 valid_end = 0;
274 break;
275 case '?':
276 if (code[i+1] == '>') {
277 i++;
278 code_type = outside;
279 break;
280 }
281 valid_end = 0;
282 break;
283 case '<':
284 valid_end = 0;
285 if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
286 i += 2;
287 code_type = heredoc_start;
288 heredoc_len = 0;
289 }
290 break;
291 default:
292 valid_end = 0;
293 break;
294 }
295 break;
296 case sstring:
297 if (code[i] == '\\') {
298 code_type = sstring_esc;
299 } else {
300 if (code[i] == '\'') {
301 code_type = body;
302 }
303 }
304 break;
305 case sstring_esc:
306 code_type = sstring;
307 break;
308 case dstring:
309 if (code[i] == '\\') {
310 code_type = dstring_esc;
311 } else {
312 if (code[i] == '"') {
313 code_type = body;
314 }
315 }
316 break;
317 case dstring_esc:
318 code_type = dstring;
319 break;
320 case comment_line:
321 if (code[i] == '\n') {
322 code_type = body;
323 }
324 break;
325 case comment_block:
326 if (code[i-1] == '*' && code[i] == '/') {
327 code_type = body;
328 valid_end = last_valid_end;
329 }
330 break;
331 case heredoc_start:
332 switch(code[i]) {
333 case ' ':
334 case '\t':
335 case '\'':
336 break;
337 case '\r':
338 case '\n':
339 code_type = heredoc;
340 break;
341 default:
342 if (!heredoc_len) {
343 heredoc_tag = code+i;
344 }
345 heredoc_len++;
346 break;
347 }
348 break;
349 case heredoc:
350 if (!strncmp(code + i - heredoc_len + 1, heredoc_tag, heredoc_len)) {
351 unsigned char c = code[i + 1];
352 char *p = code + i - heredoc_len;
353
354 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c >= 0x80) break;
355 while (*p == ' ' || *p == '\t') p--;
356 if (*p != '\n') break;
357 code_type = body;
358 }
359 break;
360 case outside:
361 if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
362 || (i > 3 && !strncmp(code+i-4, "<?php", 5))
363 ) {
364 code_type = body;
365 }
366 break;
367 }
368 }
369
370 switch (code_type) {
371 default:
372 if (brace_count) {
373 *prompt = cli_get_prompt("php", '(');
374 } else if (brackets_count) {
375 *prompt = cli_get_prompt("php", '{');
376 } else {
377 *prompt = cli_get_prompt("php", '>');
378 }
379 break;
380 case sstring:
381 case sstring_esc:
382 *prompt = cli_get_prompt("php", '\'');
383 break;
384 case dstring:
385 case dstring_esc:
386 *prompt = cli_get_prompt("php", '"');
387 break;
388 case comment_block:
389 *prompt = cli_get_prompt("/* ", '>');
390 break;
391 case heredoc:
392 *prompt = cli_get_prompt("<<<", '>');
393 break;
394 case outside:
395 *prompt = cli_get_prompt(" ", '>');
396 break;
397 }
398
399 if (!valid_end || brackets_count) {
400 return 0;
401 } else {
402 return 1;
403 }
404 }
405 /* }}} */
406
cli_completion_generator_ht(const char * text,size_t textlen,int * state,HashTable * ht,void ** pData)407 static char *cli_completion_generator_ht(const char *text, size_t textlen, int *state, HashTable *ht, void **pData) /* {{{ */
408 {
409 zend_string *name;
410 zend_ulong number;
411
412 if (!(*state % 2)) {
413 zend_hash_internal_pointer_reset(ht);
414 (*state)++;
415 }
416 while(zend_hash_has_more_elements(ht) == SUCCESS) {
417 zend_hash_get_current_key(ht, &name, &number);
418 if (!textlen || !strncmp(ZSTR_VAL(name), text, textlen)) {
419 if (pData) {
420 *pData = zend_hash_get_current_data_ptr(ht);
421 }
422 zend_hash_move_forward(ht);
423 return ZSTR_VAL(name);
424 }
425 if (zend_hash_move_forward(ht) == FAILURE) {
426 break;
427 }
428 }
429 (*state)++;
430 return NULL;
431 } /* }}} */
432
cli_completion_generator_var(const char * text,size_t textlen,int * state)433 static char *cli_completion_generator_var(const char *text, size_t textlen, int *state) /* {{{ */
434 {
435 char *retval, *tmp;
436 zend_array *symbol_table = &EG(symbol_table);
437
438 tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, symbol_table, NULL);
439 if (retval) {
440 retval = malloc(strlen(tmp) + 2);
441 retval[0] = '$';
442 strcpy(&retval[1], tmp);
443 rl_completion_append_character = '\0';
444 }
445 return retval;
446 } /* }}} */
447
cli_completion_generator_ini(const char * text,size_t textlen,int * state)448 static char *cli_completion_generator_ini(const char *text, size_t textlen, int *state) /* {{{ */
449 {
450 char *retval, *tmp;
451
452 tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(ini_directives), NULL);
453 if (retval) {
454 retval = malloc(strlen(tmp) + 2);
455 retval[0] = '#';
456 strcpy(&retval[1], tmp);
457 rl_completion_append_character = '=';
458 }
459 return retval;
460 } /* }}} */
461
cli_completion_generator_func(const char * text,size_t textlen,int * state,HashTable * ht)462 static char *cli_completion_generator_func(const char *text, size_t textlen, int *state, HashTable *ht) /* {{{ */
463 {
464 zend_function *func;
465 char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func);
466 if (retval) {
467 rl_completion_append_character = '(';
468 retval = strdup(ZSTR_VAL(func->common.function_name));
469 }
470
471 return retval;
472 } /* }}} */
473
cli_completion_generator_class(const char * text,size_t textlen,int * state)474 static char *cli_completion_generator_class(const char *text, size_t textlen, int *state) /* {{{ */
475 {
476 zend_class_entry *ce;
477 char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&ce);
478 if (retval) {
479 rl_completion_append_character = '\0';
480 retval = strdup(ZSTR_VAL(ce->name));
481 }
482
483 return retval;
484 } /* }}} */
485
cli_completion_generator_define(const char * text,size_t textlen,int * state,HashTable * ht)486 static char *cli_completion_generator_define(const char *text, size_t textlen, int *state, HashTable *ht) /* {{{ */
487 {
488 zend_class_entry **pce;
489 char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce);
490 if (retval) {
491 rl_completion_append_character = '\0';
492 retval = strdup(retval);
493 }
494
495 return retval;
496 } /* }}} */
497
498 static int cli_completion_state;
499
cli_completion_generator(const char * text,int index)500 static char *cli_completion_generator(const char *text, int index) /* {{{ */
501 {
502 /*
503 TODO:
504 - constants
505 - maybe array keys
506 - language constructs and other things outside a hashtable (echo, try, function, class, ...)
507 - object/class members
508
509 - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
510 */
511 char *retval = NULL;
512 size_t textlen = strlen(text);
513
514 if (!index) {
515 cli_completion_state = 0;
516 }
517 if (text[0] == '$') {
518 retval = cli_completion_generator_var(text, textlen, &cli_completion_state);
519 } else if (text[0] == '#') {
520 retval = cli_completion_generator_ini(text, textlen, &cli_completion_state);
521 } else {
522 char *lc_text, *class_name_end;
523 size_t class_name_len;
524 zend_string *class_name;
525 zend_class_entry *ce = NULL;
526
527 class_name_end = strstr(text, "::");
528 if (class_name_end) {
529 class_name_len = class_name_end - text;
530 class_name = zend_string_alloc(class_name_len, 0);
531 zend_str_tolower_copy(ZSTR_VAL(class_name), text, class_name_len);
532 if ((ce = zend_lookup_class(class_name)) == NULL) {
533 zend_string_release_ex(class_name, 0);
534 return NULL;
535 }
536 lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
537 textlen -= (class_name_len + 2);
538 } else {
539 lc_text = zend_str_tolower_dup(text, textlen);
540 }
541
542 switch (cli_completion_state) {
543 case 0:
544 case 1:
545 retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, ce ? &ce->function_table : EG(function_table));
546 if (retval) {
547 break;
548 }
549 case 2:
550 case 3:
551 retval = cli_completion_generator_define(text, textlen, &cli_completion_state, ce ? &ce->constants_table : EG(zend_constants));
552 if (retval || ce) {
553 break;
554 }
555 case 4:
556 case 5:
557 retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state);
558 break;
559 default:
560 break;
561 }
562 efree(lc_text);
563 if (class_name_end) {
564 zend_string_release_ex(class_name, 0);
565 }
566 if (ce && retval) {
567 size_t len = class_name_len + 2 + strlen(retval) + 1;
568 char *tmp = malloc(len);
569
570 snprintf(tmp, len, "%s::%s", ZSTR_VAL(ce->name), retval);
571 free(retval);
572 retval = tmp;
573 }
574 }
575
576 return retval;
577 } /* }}} */
578
cli_code_completion(const char * text,int start,int end)579 static char **cli_code_completion(const char *text, int start, int end) /* {{{ */
580 {
581 return rl_completion_matches(text, cli_completion_generator);
582 }
583 /* }}} */
584
readline_shell_run(void)585 static int readline_shell_run(void) /* {{{ */
586 {
587 char *line;
588 size_t size = 4096, pos = 0, len;
589 char *code = emalloc(size);
590 zend_string *prompt = cli_get_prompt("php", '>');
591 char *history_file;
592 int history_lines_to_write = 0;
593
594 if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) {
595 zend_file_handle *prepend_file_p;
596 zend_file_handle prepend_file;
597
598 memset(&prepend_file, 0, sizeof(prepend_file));
599 prepend_file.filename = PG(auto_prepend_file);
600 prepend_file.opened_path = NULL;
601 prepend_file.free_filename = 0;
602 prepend_file.type = ZEND_HANDLE_FILENAME;
603 prepend_file_p = &prepend_file;
604
605 zend_execute_scripts(ZEND_REQUIRE, NULL, 1, prepend_file_p);
606 }
607
608 #ifndef PHP_WIN32
609 history_file = tilde_expand("~/.php_history");
610 #else
611 spprintf(&history_file, MAX_PATH, "%s/.php_history", getenv("USERPROFILE"));
612 #endif
613 rl_attempted_completion_function = cli_code_completion;
614 #ifndef PHP_WIN32
615 rl_special_prefixes = "$";
616 #endif
617 read_history(history_file);
618
619 EG(exit_status) = 0;
620 while ((line = readline(ZSTR_VAL(prompt))) != NULL) {
621 if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) {
622 free(line);
623 break;
624 }
625
626 if (!pos && !*line) {
627 free(line);
628 continue;
629 }
630
631 len = strlen(line);
632
633 if (line[0] == '#') {
634 char *param = strstr(&line[1], "=");
635 if (param) {
636 zend_string *cmd;
637 param++;
638 cmd = zend_string_init(&line[1], param - &line[1] - 1, 0);
639
640 zend_alter_ini_entry_chars_ex(cmd, param, strlen(param), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
641 zend_string_release_ex(cmd, 0);
642 add_history(line);
643
644 zend_string_release_ex(prompt, 0);
645 /* TODO: This might be wrong! */
646 prompt = cli_get_prompt("php", '>');
647 continue;
648 }
649 }
650
651 if (pos + len + 2 > size) {
652 size = pos + len + 2;
653 code = erealloc(code, size);
654 }
655 memcpy(&code[pos], line, len);
656 pos += len;
657 code[pos] = '\n';
658 code[++pos] = '\0';
659
660 if (*line) {
661 add_history(line);
662 history_lines_to_write += 1;
663 }
664
665 free(line);
666 zend_string_release_ex(prompt, 0);
667
668 if (!cli_is_valid_code(code, pos, &prompt)) {
669 continue;
670 }
671
672 if (history_lines_to_write) {
673 #if HAVE_LIBEDIT
674 write_history(history_file);
675 #else
676 append_history(history_lines_to_write, history_file);
677 #endif
678 history_lines_to_write = 0;
679 }
680
681 zend_try {
682 zend_eval_stringl(code, pos, NULL, "php shell code");
683 } zend_end_try();
684
685 pos = 0;
686
687 if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') {
688 php_write("\n", 1);
689 }
690
691 if (EG(exception)) {
692 zend_exception_error(EG(exception), E_WARNING);
693 }
694
695 if (pager_pipe) {
696 fclose(pager_pipe);
697 pager_pipe = NULL;
698 }
699
700 php_last_char = '\0';
701 }
702 #ifdef PHP_WIN32
703 efree(history_file);
704 #else
705 free(history_file);
706 #endif
707 efree(code);
708 zend_string_release_ex(prompt, 0);
709 return EG(exit_status);
710 }
711 /* }}} */
712
713 #ifdef PHP_WIN32
714 typedef cli_shell_callbacks_t *(__cdecl *get_cli_shell_callbacks)(void);
715 #define GET_SHELL_CB(cb) \
716 do { \
717 get_cli_shell_callbacks get_callbacks; \
718 HMODULE hMod = GetModuleHandle("php.exe"); \
719 (cb) = NULL; \
720 if (strlen(sapi_module.name) >= 3 && 0 == strncmp("cli", sapi_module.name, 3)) { \
721 get_callbacks = (get_cli_shell_callbacks)GetProcAddress(hMod, "php_cli_get_shell_callbacks"); \
722 if (get_callbacks) { \
723 (cb) = get_callbacks(); \
724 } \
725 } \
726 } while(0)
727
728 #else
729 /*
730 #ifdef COMPILE_DL_READLINE
731 This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only
732 extensions. If that is being changed dlsym() should only be used when building
733 this extension sharedto offer compatibility.
734 */
735 #define GET_SHELL_CB(cb) \
736 do { \
737 (cb) = NULL; \
738 cli_shell_callbacks_t *(*get_callbacks)(); \
739 get_callbacks = dlsym(RTLD_DEFAULT, "php_cli_get_shell_callbacks"); \
740 if (get_callbacks) { \
741 (cb) = get_callbacks(); \
742 } \
743 } while(0)
744 /*#else
745 #define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks()
746 #endif*/
747 #endif
748
PHP_MINIT_FUNCTION(cli_readline)749 PHP_MINIT_FUNCTION(cli_readline)
750 {
751 cli_shell_callbacks_t *cb;
752
753 ZEND_INIT_MODULE_GLOBALS(cli_readline, cli_readline_init_globals, NULL);
754 REGISTER_INI_ENTRIES();
755
756 #if HAVE_LIBEDIT
757 REGISTER_STRING_CONSTANT("READLINE_LIB", "libedit", CONST_CS|CONST_PERSISTENT);
758 #else
759 REGISTER_STRING_CONSTANT("READLINE_LIB", "readline", CONST_CS|CONST_PERSISTENT);
760 #endif
761
762 GET_SHELL_CB(cb);
763 if (cb) {
764 cb->cli_shell_write = readline_shell_write;
765 cb->cli_shell_ub_write = readline_shell_ub_write;
766 cb->cli_shell_run = readline_shell_run;
767 }
768
769 return SUCCESS;
770 }
771
PHP_MSHUTDOWN_FUNCTION(cli_readline)772 PHP_MSHUTDOWN_FUNCTION(cli_readline)
773 {
774 cli_shell_callbacks_t *cb;
775
776 UNREGISTER_INI_ENTRIES();
777
778 GET_SHELL_CB(cb);
779 if (cb) {
780 cb->cli_shell_write = NULL;
781 cb->cli_shell_ub_write = NULL;
782 cb->cli_shell_run = NULL;
783 }
784
785 return SUCCESS;
786 }
787
PHP_MINFO_FUNCTION(cli_readline)788 PHP_MINFO_FUNCTION(cli_readline)
789 {
790 php_info_print_table_start();
791 php_info_print_table_header(2, "Readline Support", "enabled");
792 #ifdef PHP_WIN32
793 php_info_print_table_row(2, "Readline library", "WinEditLine");
794 #else
795 php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown"));
796 #endif
797 php_info_print_table_end();
798
799 DISPLAY_INI_ENTRIES();
800 }
801
802 /*
803 * Local variables:
804 * tab-width: 4
805 * c-basic-offset: 4
806 * End:
807 * vim600: sw=4 ts=4 fdm=marker
808 * vim<600: sw=4 ts=4
809 */
810