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

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