Back to index

php5  5.3.10
php_cli_readline.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Marcus Boerger <helly@php.net>                               |
00016    |         Johannes Schlueter <johannes@php.net>                        |
00017    +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: php_cli_readline.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #include "php.h"
00023 
00024 #if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE)
00025 
00026 #ifndef HAVE_RL_COMPLETION_MATCHES
00027 #define rl_completion_matches completion_matches
00028 #endif
00029 
00030 #include "php_globals.h"
00031 #include "php_variables.h"
00032 #include "zend_hash.h"
00033 #include "zend_modules.h"
00034 
00035 #include "SAPI.h"
00036 
00037 #if HAVE_SETLOCALE
00038 #include <locale.h>
00039 #endif
00040 #include "zend.h"
00041 #include "zend_extensions.h"
00042 #include "php_ini.h"
00043 #include "php_globals.h"
00044 #include "php_main.h"
00045 #include "fopen_wrappers.h"
00046 #include "ext/standard/php_standard.h"
00047 
00048 #ifdef __riscos__
00049 #include <unixlib/local.h>
00050 #endif
00051 
00052 #if HAVE_LIBEDIT
00053 #include <editline/readline.h>
00054 #else
00055 #include <readline/readline.h>
00056 #include <readline/history.h>
00057 #endif
00058 
00059 #include "zend_compile.h"
00060 #include "zend_execute.h"
00061 #include "zend_highlight.h"
00062 #include "zend_indent.h"
00063 
00064 typedef enum {
00065        body,
00066        sstring,
00067        dstring,
00068        sstring_esc,
00069        dstring_esc,
00070        comment_line,
00071        comment_block,
00072        heredoc_start,
00073        heredoc,
00074        outside,
00075 } php_code_type;
00076 
00077 int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */
00078 {
00079        int valid_end = 1, last_valid_end;
00080        int brackets_count = 0;
00081        int brace_count = 0;
00082        int i;
00083        php_code_type code_type = body;
00084        char *heredoc_tag;
00085        int heredoc_len;
00086 
00087        for (i = 0; i < len; ++i) {
00088               switch(code_type) {
00089                      default:
00090                             switch(code[i]) {
00091                                    case '{':
00092                                           brackets_count++;
00093                                           valid_end = 0;
00094                                           break;
00095                                    case '}':
00096                                           if (brackets_count > 0) {
00097                                                  brackets_count--;
00098                                           }
00099                                           valid_end = brackets_count ? 0 : 1;
00100                                           break;
00101                                    case '(':
00102                                           brace_count++;
00103                                           valid_end = 0;
00104                                           break;
00105                                    case ')':
00106                                           if (brace_count > 0) {
00107                                                  brace_count--;
00108                                           }
00109                                           valid_end = 0;
00110                                           break;
00111                                    case ';':
00112                                           valid_end = brace_count == 0 && brackets_count == 0;
00113                                           break;
00114                                    case ' ':
00115                                    case '\r':
00116                                    case '\n':
00117                                    case '\t':
00118                                           break;
00119                                    case '\'':
00120                                           code_type = sstring;
00121                                           break;
00122                                    case '"':
00123                                           code_type = dstring;
00124                                           break;
00125                                    case '#':
00126                                           code_type = comment_line;
00127                                           break;
00128                                    case '/':
00129                                           if (code[i+1] == '/') {
00130                                                  i++;
00131                                                  code_type = comment_line;
00132                                                  break;
00133                                           }
00134                                           if (code[i+1] == '*') {
00135                                                  last_valid_end = valid_end;
00136                                                  valid_end = 0;
00137                                                  code_type = comment_block;
00138                                                  i++;
00139                                                  break;
00140                                           }
00141                                           valid_end = 0;
00142                                           break;
00143                                    case '%':
00144                                           if (!CG(asp_tags)) {
00145                                                  valid_end = 0;
00146                                                  break;
00147                                           }
00148                                           /* no break */
00149                                    case '?':
00150                                           if (code[i+1] == '>') {
00151                                                  i++;
00152                                                  code_type = outside;
00153                                                  break;
00154                                           }
00155                                           valid_end = 0;
00156                                           break;
00157                                    case '<':
00158                                           valid_end = 0;
00159                                           if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
00160                                                  i += 2;
00161                                                  code_type = heredoc_start;
00162                                                  heredoc_len = 0;
00163                                           }
00164                                           break;
00165                                    default:
00166                                           valid_end = 0;
00167                                           break;
00168                             }
00169                             break;
00170                      case sstring:
00171                             if (code[i] == '\\') {
00172                                    code_type = sstring_esc;
00173                             } else {
00174                                    if (code[i] == '\'') {
00175                                           code_type = body;
00176                                    }
00177                             }
00178                             break;
00179                      case sstring_esc:
00180                             code_type = sstring;
00181                             break;
00182                      case dstring:
00183                             if (code[i] == '\\') {
00184                                    code_type = dstring_esc;
00185                             } else {
00186                                    if (code[i] == '"') {
00187                                           code_type = body;
00188                                    }
00189                             }
00190                             break;
00191                      case dstring_esc:
00192                             code_type = dstring;
00193                             break;
00194                      case comment_line:
00195                             if (code[i] == '\n') {
00196                                    code_type = body;
00197                             }
00198                             break;
00199                      case comment_block:
00200                             if (code[i-1] == '*' && code[i] == '/') {
00201                                    code_type = body;
00202                                    valid_end = last_valid_end;
00203                             }
00204                             break;
00205                      case heredoc_start:
00206                             switch(code[i]) {
00207                                    case ' ':
00208                                    case '\t':
00209                                           break;
00210                                    case '\r':
00211                                    case '\n':
00212                                           code_type = heredoc;
00213                                           break;
00214                                    default:
00215                                           if (!heredoc_len) {
00216                                                  heredoc_tag = code+i;
00217                                           }
00218                                           heredoc_len++;
00219                                           break;
00220                             }
00221                             break;
00222                      case heredoc:
00223                             if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {
00224                                    code_type = body;
00225                             } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') {
00226                                    code_type = body;
00227                                    valid_end = 1;
00228                             }
00229                             break;
00230                      case outside:
00231                             if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
00232                             ||  (CG(asp_tags) && !strncmp(code+i-1, "<%", 2))
00233                             ||  (i > 3 && !strncmp(code+i-4, "<?php", 5))
00234                             ) {
00235                                    code_type = body;
00236                             }
00237                             break;
00238               }
00239        }
00240 
00241        switch (code_type) {
00242               default:
00243                      if (brace_count) {
00244                             *prompt = "php ( ";
00245                      } else if (brackets_count) {
00246                             *prompt = "php { ";
00247                      } else {
00248                             *prompt = "php > ";
00249                      }
00250                      break;
00251               case sstring:
00252               case sstring_esc:
00253                      *prompt = "php ' ";
00254                      break;
00255               case dstring:
00256               case dstring_esc:
00257                      *prompt = "php \" ";
00258                      break;
00259               case comment_block:
00260                      *prompt = "/*  > ";
00261                      break;
00262               case heredoc:
00263                      *prompt = "<<< > ";
00264                      break;
00265               case outside:
00266                      *prompt = "    > ";
00267                      break;
00268        }
00269 
00270        if (!valid_end || brackets_count) {
00271               return 0;
00272        } else {
00273               return 1;
00274        }
00275 }
00276 /* }}} */
00277 
00278 static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */
00279 {
00280        char *name;
00281        ulong number;
00282 
00283        if (!(*state % 2)) {
00284               zend_hash_internal_pointer_reset(ht);
00285               (*state)++;
00286        }
00287        while(zend_hash_has_more_elements(ht) == SUCCESS) {
00288               zend_hash_get_current_key(ht, &name, &number, 0);
00289               if (!textlen || !strncmp(name, text, textlen)) {
00290                      if (pData) {
00291                             zend_hash_get_current_data(ht, pData);
00292                      }
00293                      zend_hash_move_forward(ht);
00294                      return name;
00295               }
00296               if (zend_hash_move_forward(ht) == FAILURE) {
00297                      break;
00298               }
00299        }
00300        (*state)++;
00301        return NULL;
00302 } /* }}} */
00303 
00304 static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
00305 {
00306        char *retval, *tmp;
00307 
00308        tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC);
00309        if (retval) {
00310               retval = malloc(strlen(tmp) + 2);
00311               retval[0] = '$';
00312               strcpy(&retval[1], tmp);
00313               rl_completion_append_character = '\0';
00314        }
00315        return retval;
00316 } /* }}} */
00317 
00318 static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
00319 {
00320        zend_function *func;
00321        char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);
00322        if (retval) {
00323               rl_completion_append_character = '(';
00324               retval = strdup(func->common.function_name);
00325        }
00326        
00327        return retval;
00328 } /* }}} */
00329 
00330 static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
00331 {
00332        zend_class_entry **pce;
00333        char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);
00334        if (retval) {
00335               rl_completion_append_character = '\0';
00336               retval = strdup((*pce)->name);
00337        }
00338        
00339        return retval;
00340 } /* }}} */
00341 
00342 static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
00343 {
00344        zend_class_entry **pce;
00345        char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);
00346        if (retval) {
00347               rl_completion_append_character = '\0';
00348               retval = strdup(retval);
00349        }
00350        
00351        return retval;
00352 } /* }}} */
00353 
00354 static int cli_completion_state;
00355 
00356 static char *cli_completion_generator(const char *text, int index) /* {{{ */
00357 {
00358 /*
00359 TODO:
00360 - constants
00361 - maybe array keys
00362 - language constructs and other things outside a hashtable (echo, try, function, class, ...)
00363 - object/class members
00364 
00365 - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
00366 */
00367        char *retval = NULL;
00368        int textlen = strlen(text);
00369        TSRMLS_FETCH();
00370 
00371        if (!index) {
00372               cli_completion_state = 0;
00373        }
00374        if (text[0] == '$') {
00375               retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC);
00376        } else {
00377               char *lc_text, *class_name, *class_name_end;
00378               int class_name_len;
00379               zend_class_entry **pce = NULL;
00380               
00381               class_name_end = strstr(text, "::");
00382               if (class_name_end) {
00383                      class_name_len = class_name_end - text;
00384                      class_name = zend_str_tolower_dup(text, class_name_len);
00385                      class_name[class_name_len] = '\0'; /* not done automatically */
00386                      if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {
00387                             efree(class_name);
00388                             return NULL;
00389                      }
00390                      lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
00391                      textlen -= (class_name_len + 2);
00392               } else {
00393                      lc_text = zend_str_tolower_dup(text, textlen);
00394               }
00395 
00396               switch (cli_completion_state) {
00397                      case 0:
00398                      case 1:
00399                             retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);
00400                             if (retval) {
00401                                    break;
00402                             }
00403                      case 2:
00404                      case 3:
00405                             retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);
00406                             if (retval || pce) {
00407                                    break;
00408                             }
00409                      case 4:
00410                      case 5:
00411                             retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);
00412                             break;
00413                      default:
00414                             break;
00415               }
00416               efree(lc_text);
00417               if (class_name_end) {
00418                      efree(class_name);
00419               }
00420               if (pce && retval) {
00421                      int len = class_name_len + 2 + strlen(retval) + 1;
00422                      char *tmp = malloc(len);
00423                      
00424                      snprintf(tmp, len, "%s::%s", (*pce)->name, retval);
00425                      free(retval);
00426                      retval = tmp;
00427               }
00428        }
00429        
00430        return retval;
00431 } /* }}} */
00432 
00433 char **cli_code_completion(const char *text, int start, int end) /* {{{ */
00434 {
00435        return rl_completion_matches(text, cli_completion_generator);
00436 }
00437 /* }}} */
00438 
00439 #endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
00440 
00441 /*
00442  * Local variables:
00443  * tab-width: 4
00444  * c-basic-offset: 4
00445  * End:
00446  * vim600: sw=4 ts=4 fdm=marker
00447  * vim<600: sw=4 ts=4
00448  */