PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - var/php_gcov/PHP_HEAD/sapi/cli - php_cli_readline.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 210
Code covered: 59.5 % Executed lines: 125
Legend: not executed executed

       1                 : /*
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 6                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2009 The PHP Group                                |
       6                 :    +----------------------------------------------------------------------+
       7                 :    | This source file is subject to version 3.01 of the PHP license,      |
       8                 :    | that is bundled with this package in the file LICENSE, and is        |
       9                 :    | available through the world-wide-web at the following url:           |
      10                 :    | http://www.php.net/license/3_01.txt                                  |
      11                 :    | If you did not receive a copy of the PHP license and are unable to   |
      12                 :    | obtain it through the world-wide-web, please send a note to          |
      13                 :    | license@php.net so we can mail you a copy immediately.               |
      14                 :    +----------------------------------------------------------------------+
      15                 :    | Author: Marcus Boerger <helly@php.net>                               |
      16                 :    |         Johannes Schlueter <johannes@php.net>                        |
      17                 :    +----------------------------------------------------------------------+
      18                 : */
      19                 : 
      20                 : /* $Id: php_cli_readline.c 277888 2009-03-27 19:50:56Z felipe $ */
      21                 : 
      22                 : #include "php.h"
      23                 : 
      24                 : #if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE)
      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                 : 
      48                 : #ifdef __riscos__
      49                 : #include <unixlib/local.h>
      50                 : #endif
      51                 : 
      52                 : #include <readline/readline.h>
      53                 : #if !HAVE_LIBEDIT
      54                 : #include <readline/history.h>
      55                 : #endif
      56                 : 
      57                 : #include "zend_compile.h"
      58                 : #include "zend_execute.h"
      59                 : #include "zend_highlight.h"
      60                 : #include "zend_indent.h"
      61                 : 
      62                 : /* {{{ cli_is_valid_code
      63                 :  */
      64                 : typedef enum {
      65                 :         body,
      66                 :         sstring,
      67                 :         dstring,
      68                 :         sstring_esc,
      69                 :         dstring_esc,
      70                 :         comment_line,
      71                 :         comment_block,
      72                 :         heredoc_start,
      73                 :         heredoc,
      74                 :         outside,
      75                 : } php_code_type;
      76                 : 
      77                 : int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC)
      78              18 : {
      79              18 :         int valid_end = 1, last_valid_end;
      80              18 :         int brackets_count = 0;
      81              18 :         int brace_count = 0;
      82                 :         int i;
      83              18 :         php_code_type code_type = body;
      84                 :         char *heredoc_tag;
      85                 :         int heredoc_len;
      86                 : 
      87             558 :         for (i = 0; i < len; ++i) {
      88             540 :                 switch(code_type) {
      89                 :                         default:
      90             297 :                                 switch(code[i]) {
      91                 :                                         case '{':
      92               6 :                                                 brackets_count++;
      93               6 :                                                 valid_end = 0;
      94               6 :                                                 break;
      95                 :                                         case '}':
      96               2 :                                                 if (brackets_count > 0) {
      97               2 :                                                         brackets_count--;
      98                 :                                                 }
      99               2 :                                                 valid_end = brackets_count ? 0 : 1;
     100               2 :                                                 break;
     101                 :                                         case '(':
     102               7 :                                                 brace_count++;
     103               7 :                                                 valid_end = 0;
     104               7 :                                                 break;
     105                 :                                         case ')':
     106               7 :                                                 if (brace_count > 0) {
     107               7 :                                                         brace_count--;
     108                 :                                                 }
     109               7 :                                                 valid_end = 0;
     110               7 :                                                 break;
     111                 :                                         case ';':
     112               8 :                                                 valid_end = brace_count == 0 && brackets_count == 0;
     113               8 :                                                 break;
     114                 :                                         case ' ':
     115                 :                                         case '\r':
     116                 :                                         case '\n':
     117                 :                                         case '\t':
     118              59 :                                                 break;
     119                 :                                         case '\'':
     120               4 :                                                 code_type = sstring;
     121               4 :                                                 break;
     122                 :                                         case '"':
     123               5 :                                                 code_type = dstring;
     124               5 :                                                 break;
     125                 :                                         case '#':
     126               0 :                                                 code_type = comment_line;
     127               0 :                                                 break;
     128                 :                                         case '/':
     129               0 :                                                 if (code[i+1] == '/') {
     130               0 :                                                         i++;
     131               0 :                                                         code_type = comment_line;
     132               0 :                                                         break;
     133                 :                                                 }
     134               0 :                                                 if (code[i+1] == '*') {
     135               0 :                                                         last_valid_end = valid_end;
     136               0 :                                                         valid_end = 0;
     137               0 :                                                         code_type = comment_block;
     138               0 :                                                         i++;
     139               0 :                                                         break;
     140                 :                                                 }
     141               0 :                                                 valid_end = 0;
     142               0 :                                                 break;
     143                 :                                         case '%':
     144               0 :                                                 if (!CG(asp_tags)) {
     145               0 :                                                         valid_end = 0;
     146               0 :                                                         break;
     147                 :                                                 }
     148                 :                                                 /* no break */
     149                 :                                         case '?':
     150               0 :                                                 if (code[i+1] == '>') {
     151               0 :                                                         i++;
     152               0 :                                                         code_type = outside;
     153               0 :                                                         break;
     154                 :                                                 }
     155               0 :                                                 valid_end = 0;
     156               0 :                                                 break;
     157                 :                                         case '<':
     158               6 :                                                 valid_end = 0;
     159               6 :                                                 if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
     160               6 :                                                         i += 2;
     161               6 :                                                         code_type = heredoc_start;
     162               6 :                                                         heredoc_len = 0;
     163                 :                                                 }
     164               6 :                                                 break;
     165                 :                                         default:
     166             193 :                                                 valid_end = 0;
     167                 :                                                 break;
     168                 :                                 }
     169             297 :                                 break;
     170                 :                         case sstring:
     171              56 :                                 if (code[i] == '\\') {
     172               0 :                                         code_type = sstring_esc;
     173                 :                                 } else {
     174              56 :                                         if (code[i] == '\'') {
     175               2 :                                                 code_type = body;
     176                 :                                         }
     177                 :                                 }
     178              56 :                                 break;
     179                 :                         case sstring_esc:
     180               0 :                                 code_type = sstring;
     181               0 :                                 break;
     182                 :                         case dstring:
     183              61 :                                 if (code[i] == '\\') {
     184               0 :                                         code_type = dstring_esc;
     185                 :                                 } else {
     186              61 :                                         if (code[i] == '"') {
     187               5 :                                                 code_type = body;
     188                 :                                         }
     189                 :                                 }
     190              61 :                                 break;
     191                 :                         case dstring_esc:
     192               0 :                                 code_type = dstring;
     193               0 :                                 break;
     194                 :                         case comment_line:
     195               0 :                                 if (code[i] == '\n') {
     196               0 :                                         code_type = body;
     197                 :                                 }
     198               0 :                                 break;
     199                 :                         case comment_block:
     200               0 :                                 if (code[i-1] == '*' && code[i] == '/') {
     201               0 :                                         code_type = body;
     202               0 :                                         valid_end = last_valid_end;
     203                 :                                 }
     204               0 :                                 break;
     205                 :                         case heredoc_start:
     206              48 :                                 switch(code[i]) {
     207                 :                                         case ' ':
     208                 :                                         case '\t':
     209               0 :                                                 break;
     210                 :                                         case '\r':
     211                 :                                         case '\n':
     212               6 :                                                 code_type = heredoc;
     213               6 :                                                 break;
     214                 :                                         default:
     215              42 :                                                 if (!heredoc_len) {
     216               6 :                                                         heredoc_tag = code+i;
     217                 :                                                 }
     218              42 :                                                 heredoc_len++;
     219                 :                                                 break;
     220                 :                                 }
     221              48 :                                 break;
     222                 :                         case heredoc:
     223              78 :                                 if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {
     224               0 :                                         code_type = body;
     225              78 :                                 } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') {
     226               1 :                                         code_type = body;
     227               1 :                                         valid_end = 1;
     228                 :                                 }
     229              78 :                                 break;
     230                 :                         case outside:
     231               0 :                                 if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
     232                 :                                 ||  (CG(asp_tags) && !strncmp(code+i-1, "<%", 2))
     233                 :                                 ||  (i > 3 && !strncmp(code+i-4, "<?php", 5))
     234                 :                                 ) {
     235               0 :                                         code_type = body;
     236                 :                                 }
     237                 :                                 break;
     238                 :                 }
     239                 :         }
     240                 : 
     241              18 :         switch (code_type) {
     242                 :                 default:
     243              11 :                         if (brace_count) {
     244               0 :                                 *prompt = "php ( ";
     245              11 :                         } else if (brackets_count) {
     246               4 :                                 *prompt = "php { ";
     247                 :                         } else {
     248               7 :                                 *prompt = "php > ";
     249                 :                         }
     250              11 :                         break;
     251                 :                 case sstring:
     252                 :                 case sstring_esc:
     253               2 :                         *prompt = "php ' ";
     254               2 :                         break;
     255                 :                 case dstring:
     256                 :                 case dstring_esc:
     257               0 :                         *prompt = "php \" ";
     258               0 :                         break;
     259                 :                 case comment_block:
     260               0 :                         *prompt = "/*  > ";
     261               0 :                         break;
     262                 :                 case heredoc:
     263               5 :                         *prompt = "<<< > ";
     264               5 :                         break;
     265                 :                 case outside:
     266               0 :                         *prompt = "    > ";
     267                 :                         break;
     268                 :         }
     269                 : 
     270              18 :         if (!valid_end || brackets_count) {
     271              11 :                 return 0;
     272                 :         } else {
     273               7 :                 return 1;
     274                 :         }
     275                 : }
     276                 : /* }}} */
     277                 : 
     278                 : static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */
     279               4 : {
     280                 :         zstr name;
     281                 :         ulong number;
     282                 : 
     283               4 :         if (!(*state % 2)) {
     284               3 :                 zend_hash_internal_pointer_reset(ht);
     285               3 :                 (*state)++;
     286                 :         }
     287            4183 :         while(zend_hash_has_more_elements(ht) == SUCCESS) {
     288            4176 :                 zend_hash_get_current_key(ht, &name, &number, 0);
     289            4176 :                 if (!textlen || !zend_cmp_unicode_and_string(name.u, (char *)text, textlen)) {
     290               1 :                         if (pData) {
     291               1 :                                 zend_hash_get_current_data(ht, pData);
     292                 :                         }
     293               1 :                         zend_hash_move_forward(ht);
     294               1 :                         return name.s;
     295                 :                 }
     296            4175 :                 if (zend_hash_move_forward(ht) == FAILURE) {
     297               0 :                         break;
     298                 :                 }
     299                 :         }
     300               3 :         (*state)++;
     301               3 :         return NULL;
     302                 : } /* }}} */
     303                 : 
     304                 : static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
     305               0 : {
     306                 :         char *retval, *tmp;
     307                 : 
     308               0 :         tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC);
     309               0 :         if (retval) {
     310                 :                 int32_t tmp_len, len;
     311               0 :                 UErrorCode status = U_ZERO_ERROR;
     312                 : 
     313               0 :                 len = u_strlen((UChar *)retval);
     314               0 :                 zend_unicode_to_string_ex(ZEND_U_CONVERTER(UG(output_encoding_conv)), &tmp, &tmp_len, 
     315                 :                                                                   (UChar *)retval, len, &status);
     316                 : 
     317               0 :                 retval = malloc(tmp_len + 2);
     318               0 :                 retval[0] = '$';
     319               0 :                 strcpy(&retval[1], tmp);
     320               0 :                 rl_completion_append_character = '\0';
     321               0 :                 efree(tmp);
     322                 :         }
     323               0 :         return retval;
     324                 : } /* }}} */
     325                 : 
     326                 : static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
     327               2 : {
     328                 :         zend_function *func;
     329                 :         char *retval;
     330                 : 
     331               2 :         retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);
     332               2 :         if (retval) {
     333                 :                 char *tmp;
     334                 :                 int32_t tmp_len, len;
     335               1 :                 UErrorCode status = U_ZERO_ERROR;
     336                 :                                 
     337               1 :                 rl_completion_append_character = '(';   
     338                 : 
     339               1 :                 len = u_strlen((UChar *)func->common.function_name.u);
     340               1 :                 zend_unicode_to_string_ex(ZEND_U_CONVERTER(UG(output_encoding_conv)), &tmp, &tmp_len,
     341                 :                                                                   (UChar *)func->common.function_name.u, len, &status);
     342                 : 
     343               1 :                 retval = strdup(tmp);
     344               1 :                 efree(tmp);
     345                 :         }
     346                 :         
     347               2 :         return retval;
     348                 : } /* }}} */
     349                 : 
     350                 : static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
     351               1 : {
     352                 :         zend_class_entry **pce;
     353               1 :         char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);
     354               1 :         if (retval) {
     355                 :                 char *tmp;
     356                 :                 int32_t tmp_len, len;
     357               0 :                 UErrorCode status = U_ZERO_ERROR;
     358                 :                         
     359               0 :                 rl_completion_append_character = '\0';
     360                 : 
     361               0 :                 len = u_strlen((UChar *)(*pce)->name.u);
     362               0 :                 zend_unicode_to_string_ex(ZEND_U_CONVERTER(UG(output_encoding_conv)), &tmp, &tmp_len, 
     363                 :                                                                   (UChar *)(*pce)->name.u, len, &status);
     364                 : 
     365               0 :                 retval = strdup(tmp);
     366               0 :                 efree(tmp);
     367                 :         }
     368                 :         
     369               1 :         return retval;
     370                 : } /* }}} */
     371                 : 
     372                 : static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
     373               1 : {
     374                 :         zend_class_entry **pce;
     375               1 :         char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);
     376               1 :         if (retval) {
     377                 :                 char *tmp;
     378                 :                 int32_t tmp_len, len;
     379               0 :                 UErrorCode status = U_ZERO_ERROR;
     380                 :                         
     381               0 :                 rl_completion_append_character = '\0';
     382                 : 
     383               0 :                 len = u_strlen((UChar *)retval);
     384               0 :                 zend_unicode_to_string_ex(ZEND_U_CONVERTER(UG(output_encoding_conv)), &tmp, &tmp_len, 
     385                 :                                                                   (UChar *)retval, len, &status);
     386                 : 
     387               0 :                 retval = strdup(tmp);
     388               0 :                 efree(tmp);
     389                 :         }
     390                 :         
     391               1 :         return retval;
     392                 : } /* }}} */
     393                 : 
     394                 : static int cli_completion_state;
     395                 : 
     396                 : static char *cli_completion_generator(const char *text, int index) /* {{{ */
     397               2 : {
     398                 : /*
     399                 : TODO:
     400                 : - constants
     401                 : - maybe array keys
     402                 : - language constructs and other things outside a hashtable (echo, try, function, class, ...)
     403                 : - object/class members
     404                 : 
     405                 : - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
     406                 : */
     407               2 :         char *retval = NULL;
     408               2 :         int textlen = strlen(text);
     409                 :         TSRMLS_FETCH();
     410                 : 
     411               2 :         if (!index) {
     412               1 :                 cli_completion_state = 0;
     413                 :         }
     414               2 :         if (text[0] == '$') {
     415               0 :                 retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC);
     416                 :         } else {
     417                 :                 char *lc_text, *class_name, *class_name_end;
     418                 :                 int class_name_len;
     419               2 :                 zend_class_entry **pce = NULL;
     420                 :                 
     421               2 :                 class_name_end = strstr(text, "::");
     422               2 :                 if (class_name_end) {
     423               0 :                         class_name_len = class_name_end - text;
     424               0 :                         class_name = zend_str_tolower_dup(text, class_name_len);
     425               0 :                         class_name[class_name_len] = '\0'; /* not done automatically */
     426               0 :                         if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {
     427               0 :                                 efree(class_name);
     428               0 :                                 return NULL;
     429                 :                         }
     430               0 :                         lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
     431               0 :                         textlen -= (class_name_len + 2);
     432                 :                 } else {
     433               2 :                         lc_text = zend_str_tolower_dup(text, textlen);
     434                 :                 }
     435                 : 
     436               2 :                 switch (cli_completion_state) {
     437                 :                         case 0:
     438                 :                         case 1:
     439               2 :                                 retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);
     440               2 :                                 if (retval) {
     441               1 :                                         break;
     442                 :                                 }
     443                 :                         case 2:
     444                 :                         case 3:
     445               1 :                                 retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);
     446               1 :                                 if (retval || pce) {
     447                 :                                         break;
     448                 :                                 }
     449                 :                         case 4:
     450                 :                         case 5:
     451               1 :                                 retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);
     452                 :                                 break;
     453                 :                         default:
     454                 :                                 break;
     455                 :                 }
     456               2 :                 efree(lc_text);
     457               2 :                 if (class_name_end) {
     458               0 :                         efree(class_name);
     459                 :                 }
     460               2 :                 if (pce && retval) {
     461               0 :                         char *tmp = malloc(class_name_len + 2 + strlen(retval) + 1);
     462                 :                         
     463               0 :                         sprintf(tmp, "%s::%s", (*pce)->name.s, retval);
     464               0 :                         free(retval);
     465               0 :                         retval = tmp;
     466                 :                 }
     467                 :         }
     468                 :         
     469               2 :         return retval;
     470                 : } /* }}} */
     471                 : 
     472                 : /* {{{ cli_code_completion
     473                 :  */
     474                 : char **cli_code_completion(const char *text, int start, int end)
     475               1 : {
     476               1 :         return rl_completion_matches(text, cli_completion_generator);
     477                 : }
     478                 : /* }}} */
     479                 : 
     480                 : #endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
     481                 : 
     482                 : /*
     483                 :  * Local variables:
     484                 :  * tab-width: 4
     485                 :  * c-basic-offset: 4
     486                 :  * End:
     487                 :  * vim600: sw=4 ts=4 fdm=marker
     488                 :  * vim<600: sw=4 ts=4
     489                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:48 +0000 (35 hours ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.