Annotation of embedaddon/php/sapi/cli/php_cli_readline.c, revision 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>