root/ext/readline/readline_cli.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. readline_shell_write
  2. readline_shell_ub_write
  3. cli_readline_init_globals
  4. PHP_INI_BEGIN
  5. cli_get_prompt
  6. cli_is_valid_code
  7. cli_completion_generator_ht
  8. cli_completion_generator_var
  9. cli_completion_generator_ini
  10. cli_completion_generator_func
  11. cli_completion_generator_class
  12. cli_completion_generator_define
  13. cli_completion_generator
  14. cli_code_completion
  15. readline_shell_run
  16. PHP_MINIT_FUNCTION
  17. PHP_MSHUTDOWN_FUNCTION
  18. PHP_MINFO_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */