Annotation of embedaddon/php/sapi/cli/php_cli_readline.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
                      5:    | Copyright (c) 1997-2012 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 321634 2012-01-01 13:15:04Z 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: #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: int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */
                     78: {
                     79:        int valid_end = 1, last_valid_end;
                     80:        int brackets_count = 0;
                     81:        int brace_count = 0;
                     82:        int i;
                     83:        php_code_type code_type = body;
                     84:        char *heredoc_tag;
                     85:        int heredoc_len;
                     86: 
                     87:        for (i = 0; i < len; ++i) {
                     88:                switch(code_type) {
                     89:                        default:
                     90:                                switch(code[i]) {
                     91:                                        case '{':
                     92:                                                brackets_count++;
                     93:                                                valid_end = 0;
                     94:                                                break;
                     95:                                        case '}':
                     96:                                                if (brackets_count > 0) {
                     97:                                                        brackets_count--;
                     98:                                                }
                     99:                                                valid_end = brackets_count ? 0 : 1;
                    100:                                                break;
                    101:                                        case '(':
                    102:                                                brace_count++;
                    103:                                                valid_end = 0;
                    104:                                                break;
                    105:                                        case ')':
                    106:                                                if (brace_count > 0) {
                    107:                                                        brace_count--;
                    108:                                                }
                    109:                                                valid_end = 0;
                    110:                                                break;
                    111:                                        case ';':
                    112:                                                valid_end = brace_count == 0 && brackets_count == 0;
                    113:                                                break;
                    114:                                        case ' ':
                    115:                                        case '\r':
                    116:                                        case '\n':
                    117:                                        case '\t':
                    118:                                                break;
                    119:                                        case '\'':
                    120:                                                code_type = sstring;
                    121:                                                break;
                    122:                                        case '"':
                    123:                                                code_type = dstring;
                    124:                                                break;
                    125:                                        case '#':
                    126:                                                code_type = comment_line;
                    127:                                                break;
                    128:                                        case '/':
                    129:                                                if (code[i+1] == '/') {
                    130:                                                        i++;
                    131:                                                        code_type = comment_line;
                    132:                                                        break;
                    133:                                                }
                    134:                                                if (code[i+1] == '*') {
                    135:                                                        last_valid_end = valid_end;
                    136:                                                        valid_end = 0;
                    137:                                                        code_type = comment_block;
                    138:                                                        i++;
                    139:                                                        break;
                    140:                                                }
                    141:                                                valid_end = 0;
                    142:                                                break;
                    143:                                        case '%':
                    144:                                                if (!CG(asp_tags)) {
                    145:                                                        valid_end = 0;
                    146:                                                        break;
                    147:                                                }
                    148:                                                /* no break */
                    149:                                        case '?':
                    150:                                                if (code[i+1] == '>') {
                    151:                                                        i++;
                    152:                                                        code_type = outside;
                    153:                                                        break;
                    154:                                                }
                    155:                                                valid_end = 0;
                    156:                                                break;
                    157:                                        case '<':
                    158:                                                valid_end = 0;
                    159:                                                if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
                    160:                                                        i += 2;
                    161:                                                        code_type = heredoc_start;
                    162:                                                        heredoc_len = 0;
                    163:                                                }
                    164:                                                break;
                    165:                                        default:
                    166:                                                valid_end = 0;
                    167:                                                break;
                    168:                                }
                    169:                                break;
                    170:                        case sstring:
                    171:                                if (code[i] == '\\') {
                    172:                                        code_type = sstring_esc;
                    173:                                } else {
                    174:                                        if (code[i] == '\'') {
                    175:                                                code_type = body;
                    176:                                        }
                    177:                                }
                    178:                                break;
                    179:                        case sstring_esc:
                    180:                                code_type = sstring;
                    181:                                break;
                    182:                        case dstring:
                    183:                                if (code[i] == '\\') {
                    184:                                        code_type = dstring_esc;
                    185:                                } else {
                    186:                                        if (code[i] == '"') {
                    187:                                                code_type = body;
                    188:                                        }
                    189:                                }
                    190:                                break;
                    191:                        case dstring_esc:
                    192:                                code_type = dstring;
                    193:                                break;
                    194:                        case comment_line:
                    195:                                if (code[i] == '\n') {
                    196:                                        code_type = body;
                    197:                                }
                    198:                                break;
                    199:                        case comment_block:
                    200:                                if (code[i-1] == '*' && code[i] == '/') {
                    201:                                        code_type = body;
                    202:                                        valid_end = last_valid_end;
                    203:                                }
                    204:                                break;
                    205:                        case heredoc_start:
                    206:                                switch(code[i]) {
                    207:                                        case ' ':
                    208:                                        case '\t':
                    209:                                                break;
                    210:                                        case '\r':
                    211:                                        case '\n':
                    212:                                                code_type = heredoc;
                    213:                                                break;
                    214:                                        default:
                    215:                                                if (!heredoc_len) {
                    216:                                                        heredoc_tag = code+i;
                    217:                                                }
                    218:                                                heredoc_len++;
                    219:                                                break;
                    220:                                }
                    221:                                break;
                    222:                        case heredoc:
                    223:                                if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {
                    224:                                        code_type = body;
                    225:                                } 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:                                        code_type = body;
                    227:                                        valid_end = 1;
                    228:                                }
                    229:                                break;
                    230:                        case outside:
                    231:                                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:                                        code_type = body;
                    236:                                }
                    237:                                break;
                    238:                }
                    239:        }
                    240: 
                    241:        switch (code_type) {
                    242:                default:
                    243:                        if (brace_count) {
                    244:                                *prompt = "php ( ";
                    245:                        } else if (brackets_count) {
                    246:                                *prompt = "php { ";
                    247:                        } else {
                    248:                                *prompt = "php > ";
                    249:                        }
                    250:                        break;
                    251:                case sstring:
                    252:                case sstring_esc:
                    253:                        *prompt = "php ' ";
                    254:                        break;
                    255:                case dstring:
                    256:                case dstring_esc:
                    257:                        *prompt = "php \" ";
                    258:                        break;
                    259:                case comment_block:
                    260:                        *prompt = "/*  > ";
                    261:                        break;
                    262:                case heredoc:
                    263:                        *prompt = "<<< > ";
                    264:                        break;
                    265:                case outside:
                    266:                        *prompt = "    > ";
                    267:                        break;
                    268:        }
                    269: 
                    270:        if (!valid_end || brackets_count) {
                    271:                return 0;
                    272:        } else {
                    273:                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: {
                    280:        char *name;
                    281:        ulong number;
                    282: 
                    283:        if (!(*state % 2)) {
                    284:                zend_hash_internal_pointer_reset(ht);
                    285:                (*state)++;
                    286:        }
                    287:        while(zend_hash_has_more_elements(ht) == SUCCESS) {
                    288:                zend_hash_get_current_key(ht, &name, &number, 0);
                    289:                if (!textlen || !strncmp(name, text, textlen)) {
                    290:                        if (pData) {
                    291:                                zend_hash_get_current_data(ht, pData);
                    292:                        }
                    293:                        zend_hash_move_forward(ht);
                    294:                        return name;
                    295:                }
                    296:                if (zend_hash_move_forward(ht) == FAILURE) {
                    297:                        break;
                    298:                }
                    299:        }
                    300:        (*state)++;
                    301:        return NULL;
                    302: } /* }}} */
                    303: 
                    304: static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
                    305: {
                    306:        char *retval, *tmp;
                    307: 
                    308:        tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC);
                    309:        if (retval) {
                    310:                retval = malloc(strlen(tmp) + 2);
                    311:                retval[0] = '$';
                    312:                strcpy(&retval[1], tmp);
                    313:                rl_completion_append_character = '\0';
                    314:        }
                    315:        return retval;
                    316: } /* }}} */
                    317: 
                    318: static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
                    319: {
                    320:        zend_function *func;
                    321:        char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);
                    322:        if (retval) {
                    323:                rl_completion_append_character = '(';
                    324:                retval = strdup(func->common.function_name);
                    325:        }
                    326:        
                    327:        return retval;
                    328: } /* }}} */
                    329: 
                    330: static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
                    331: {
                    332:        zend_class_entry **pce;
                    333:        char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);
                    334:        if (retval) {
                    335:                rl_completion_append_character = '\0';
                    336:                retval = strdup((*pce)->name);
                    337:        }
                    338:        
                    339:        return retval;
                    340: } /* }}} */
                    341: 
                    342: static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
                    343: {
                    344:        zend_class_entry **pce;
                    345:        char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);
                    346:        if (retval) {
                    347:                rl_completion_append_character = '\0';
                    348:                retval = strdup(retval);
                    349:        }
                    350:        
                    351:        return retval;
                    352: } /* }}} */
                    353: 
                    354: static int cli_completion_state;
                    355: 
                    356: 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:        char *retval = NULL;
                    368:        int textlen = strlen(text);
                    369:        TSRMLS_FETCH();
                    370: 
                    371:        if (!index) {
                    372:                cli_completion_state = 0;
                    373:        }
                    374:        if (text[0] == '$') {
                    375:                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:                zend_class_entry **pce = NULL;
                    380:                
                    381:                class_name_end = strstr(text, "::");
                    382:                if (class_name_end) {
                    383:                        class_name_len = class_name_end - text;
                    384:                        class_name = zend_str_tolower_dup(text, class_name_len);
                    385:                        class_name[class_name_len] = '\0'; /* not done automatically */
                    386:                        if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {
                    387:                                efree(class_name);
                    388:                                return NULL;
                    389:                        }
                    390:                        lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
                    391:                        textlen -= (class_name_len + 2);
                    392:                } else {
                    393:                        lc_text = zend_str_tolower_dup(text, textlen);
                    394:                }
                    395: 
                    396:                switch (cli_completion_state) {
                    397:                        case 0:
                    398:                        case 1:
                    399:                                retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);
                    400:                                if (retval) {
                    401:                                        break;
                    402:                                }
                    403:                        case 2:
                    404:                        case 3:
                    405:                                retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);
                    406:                                if (retval || pce) {
                    407:                                        break;
                    408:                                }
                    409:                        case 4:
                    410:                        case 5:
                    411:                                retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);
                    412:                                break;
                    413:                        default:
                    414:                                break;
                    415:                }
                    416:                efree(lc_text);
                    417:                if (class_name_end) {
                    418:                        efree(class_name);
                    419:                }
                    420:                if (pce && retval) {
                    421:                        int len = class_name_len + 2 + strlen(retval) + 1;
                    422:                        char *tmp = malloc(len);
                    423:                        
                    424:                        snprintf(tmp, len, "%s::%s", (*pce)->name, retval);
                    425:                        free(retval);
                    426:                        retval = tmp;
                    427:                }
                    428:        }
                    429:        
                    430:        return retval;
                    431: } /* }}} */
                    432: 
                    433: char **cli_code_completion(const char *text, int start, int end) /* {{{ */
                    434: {
                    435:        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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>