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

LCOV - code coverage report
Current view: top level - sapi/cli - php_cli_readline.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 121 197 61.4 %
Date: 2014-04-18 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | PHP Version 5                                                        |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1997-2013 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             : #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             : #if HAVE_LIBEDIT
      53             : #include <editline/readline.h>
      54             : #else
      55             : #include <readline/readline.h>
      56             : #include <readline/history.h>
      57             : #endif
      58             : 
      59             : #include "zend_compile.h"
      60             : #include "zend_execute.h"
      61             : #include "zend_highlight.h"
      62             : #include "zend_indent.h"
      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          18 : int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */
      78             : {
      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           0 :                                 ||  (CG(asp_tags) && !strncmp(code+i-1, "<%", 2))
     233           0 :                                 ||  (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           4 : static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */
     279             : {
     280             :         char *name;
     281             :         ulong number;
     282             : 
     283           4 :         if (!(*state % 2)) {
     284           3 :                 zend_hash_internal_pointer_reset(ht);
     285           3 :                 (*state)++;
     286             :         }
     287        4742 :         while(zend_hash_has_more_elements(ht) == SUCCESS) {
     288        4735 :                 zend_hash_get_current_key(ht, &name, &number, 0);
     289        4735 :                 if (!textlen || !strncmp(name, 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;
     295             :                 }
     296        4734 :                 if (zend_hash_move_forward(ht) == FAILURE) {
     297           0 :                         break;
     298             :                 }
     299             :         }
     300           3 :         (*state)++;
     301           3 :         return NULL;
     302             : } /* }}} */
     303             : 
     304           0 : static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
     305             : {
     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           0 :                 retval = malloc(strlen(tmp) + 2);
     311           0 :                 retval[0] = '$';
     312           0 :                 strcpy(&retval[1], tmp);
     313           0 :                 rl_completion_append_character = '\0';
     314             :         }
     315           0 :         return retval;
     316             : } /* }}} */
     317             : 
     318           2 : static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
     319             : {
     320             :         zend_function *func;
     321           2 :         char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);
     322           2 :         if (retval) {
     323           1 :                 rl_completion_append_character = '(';
     324           1 :                 retval = strdup(func->common.function_name);
     325             :         }
     326             :         
     327           2 :         return retval;
     328             : } /* }}} */
     329             : 
     330           1 : static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
     331             : {
     332             :         zend_class_entry **pce;
     333           1 :         char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);
     334           1 :         if (retval) {
     335           0 :                 rl_completion_append_character = '\0';
     336           0 :                 retval = strdup((*pce)->name);
     337             :         }
     338             :         
     339           1 :         return retval;
     340             : } /* }}} */
     341             : 
     342           1 : static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
     343             : {
     344             :         zend_class_entry **pce;
     345           1 :         char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);
     346           1 :         if (retval) {
     347           0 :                 rl_completion_append_character = '\0';
     348           0 :                 retval = strdup(retval);
     349             :         }
     350             :         
     351           1 :         return retval;
     352             : } /* }}} */
     353             : 
     354             : static int cli_completion_state;
     355             : 
     356           2 : static char *cli_completion_generator(const char *text, int index) /* {{{ */
     357             : {
     358             : /*
     359             : TODO:
     360             : - constants
     361             : - maybe array keys
     362             : - language constructs and other things outside a hashtable (echo, try, function, class, ...)
     363             : - object/class members
     364             : 
     365             : - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
     366             : */
     367           2 :         char *retval = NULL;
     368           2 :         int textlen = strlen(text);
     369             :         TSRMLS_FETCH();
     370             : 
     371           2 :         if (!index) {
     372           1 :                 cli_completion_state = 0;
     373             :         }
     374           2 :         if (text[0] == '$') {
     375           0 :                 retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC);
     376             :         } else {
     377             :                 char *lc_text, *class_name, *class_name_end;
     378             :                 int class_name_len;
     379           2 :                 zend_class_entry **pce = NULL;
     380             :                 
     381           2 :                 class_name_end = strstr(text, "::");
     382           2 :                 if (class_name_end) {
     383           0 :                         class_name_len = class_name_end - text;
     384           0 :                         class_name = zend_str_tolower_dup(text, class_name_len);
     385           0 :                         class_name[class_name_len] = '\0'; /* not done automatically */
     386           0 :                         if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {
     387           0 :                                 efree(class_name);
     388           0 :                                 return NULL;
     389             :                         }
     390           0 :                         lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
     391           0 :                         textlen -= (class_name_len + 2);
     392             :                 } else {
     393           2 :                         lc_text = zend_str_tolower_dup(text, textlen);
     394             :                 }
     395             : 
     396           2 :                 switch (cli_completion_state) {
     397             :                         case 0:
     398             :                         case 1:
     399           2 :                                 retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);
     400           2 :                                 if (retval) {
     401           1 :                                         break;
     402             :                                 }
     403             :                         case 2:
     404             :                         case 3:
     405           1 :                                 retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);
     406           1 :                                 if (retval || pce) {
     407             :                                         break;
     408             :                                 }
     409             :                         case 4:
     410             :                         case 5:
     411           1 :                                 retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);
     412             :                                 break;
     413             :                         default:
     414             :                                 break;
     415             :                 }
     416           2 :                 efree(lc_text);
     417           2 :                 if (class_name_end) {
     418           0 :                         efree(class_name);
     419             :                 }
     420           2 :                 if (pce && retval) {
     421           0 :                         int len = class_name_len + 2 + strlen(retval) + 1;
     422           0 :                         char *tmp = malloc(len);
     423             :                         
     424           0 :                         snprintf(tmp, len, "%s::%s", (*pce)->name, retval);
     425           0 :                         free(retval);
     426           0 :                         retval = tmp;
     427             :                 }
     428             :         }
     429             :         
     430           2 :         return retval;
     431             : } /* }}} */
     432             : 
     433           1 : char **cli_code_completion(const char *text, int start, int end) /* {{{ */
     434             : {
     435           1 :         return rl_completion_matches(text, cli_completion_generator);
     436             : }
     437             : /* }}} */
     438             : 
     439             : #endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
     440             : 
     441             : /*
     442             :  * Local variables:
     443             :  * tab-width: 4
     444             :  * c-basic-offset: 4
     445             :  * End:
     446             :  * vim600: sw=4 ts=4 fdm=marker
     447             :  * vim<600: sw=4 ts=4
     448             :  */

Generated by: LCOV version 1.10

Generated at Fri, 18 Apr 2014 07:01:42 +0000 (6 hours ago)

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