Annotation of embedaddon/php/ext/readline/readline_cli.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
1.1.1.2 ! misho       5:    | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       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: #ifndef HAVE_RL_COMPLETION_MATCHES
                     25: #define rl_completion_matches completion_matches
                     26: #endif
                     27: 
                     28: #include "php_globals.h"
                     29: #include "php_variables.h"
                     30: #include "zend_hash.h"
                     31: #include "zend_modules.h"
                     32: 
                     33: #include "SAPI.h"
                     34: 
                     35: #if HAVE_SETLOCALE
                     36: #include <locale.h>
                     37: #endif
                     38: #include "zend.h"
                     39: #include "zend_extensions.h"
                     40: #include "php_ini.h"
                     41: #include "php_globals.h"
                     42: #include "php_main.h"
                     43: #include "fopen_wrappers.h"
                     44: #include "ext/standard/php_standard.h"
                     45: #include "ext/standard/php_smart_str.h"
                     46: 
                     47: #ifdef __riscos__
                     48: #include <unixlib/local.h>
                     49: #endif
                     50: 
                     51: #if HAVE_LIBEDIT
                     52: #include <editline/readline.h>
                     53: #else
                     54: #include <readline/readline.h>
                     55: #include <readline/history.h>
                     56: #endif
                     57: 
                     58: #include "zend_compile.h"
                     59: #include "zend_execute.h"
                     60: #include "zend_highlight.h"
                     61: #include "zend_indent.h"
                     62: #include "zend_exceptions.h"
                     63: 
                     64: #include "sapi/cli/cli.h"
                     65: #include "readline_cli.h"
                     66: 
                     67: #ifdef COMPILE_DL_READLINE
                     68: #include <dlfcn.h>
                     69: #endif
                     70: 
                     71: #ifndef RTLD_DEFAULT
                     72: #define RTLD_DEFAULT NULL
                     73: #endif
                     74: 
                     75: #define DEFAULT_PROMPT "\\b \\> "
                     76: 
                     77: ZEND_DECLARE_MODULE_GLOBALS(cli_readline);
                     78: 
                     79: static char php_last_char = '\0';
                     80: static FILE *pager_pipe = NULL;
                     81: 
                     82: static size_t readline_shell_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */
                     83: {
                     84:        if (CLIR_G(prompt_str)) {
                     85:                smart_str_appendl(CLIR_G(prompt_str), str, str_length);
                     86:                return str_length;
                     87:        }
                     88: 
                     89:        if (CLIR_G(pager) && *CLIR_G(pager) && !pager_pipe) {
                     90:                pager_pipe = VCWD_POPEN(CLIR_G(pager), "w");
                     91:        }
                     92:        if (pager_pipe) {
                     93:                return fwrite(str, 1, MIN(str_length, 16384), pager_pipe);
                     94:        }
                     95: 
                     96:        return -1;
                     97: }
                     98: /* }}} */
                     99: 
                    100: static int readline_shell_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */
                    101: {
                    102:        php_last_char = str[str_length-1];
                    103:        return -1;
                    104: }
                    105: /* }}} */
                    106: 
                    107: static void cli_readline_init_globals(zend_cli_readline_globals *rg TSRMLS_DC)
                    108: {
                    109:        rg->pager = NULL;
                    110:        rg->prompt = NULL;
                    111:        rg->prompt_str = NULL;
                    112: }
                    113: 
                    114: PHP_INI_BEGIN()
                    115:        STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals)
                    116:        STD_PHP_INI_ENTRY("cli.prompt", DEFAULT_PROMPT, PHP_INI_ALL, OnUpdateString, prompt, zend_cli_readline_globals, cli_readline_globals)
                    117: PHP_INI_END()
                    118: 
                    119: 
                    120: 
                    121: typedef enum {
                    122:        body,
                    123:        sstring,
                    124:        dstring,
                    125:        sstring_esc,
                    126:        dstring_esc,
                    127:        comment_line,
                    128:        comment_block,
                    129:        heredoc_start,
                    130:        heredoc,
                    131:        outside,
                    132: } php_code_type;
                    133: 
                    134: static char *cli_get_prompt(char *block, char prompt TSRMLS_DC) /* {{{ */
                    135: {
                    136:        smart_str retval = {0};
                    137:        char *prompt_spec = CLIR_G(prompt) ? CLIR_G(prompt) : DEFAULT_PROMPT;
                    138: 
                    139:        do {
                    140:                if (*prompt_spec == '\\') {
                    141:                        switch (prompt_spec[1]) {
                    142:                        case '\\':
                    143:                                smart_str_appendc(&retval, '\\');
                    144:                                prompt_spec++;
                    145:                                break;
                    146:                        case 'n':
                    147:                                smart_str_appendc(&retval, '\n');
                    148:                                prompt_spec++;
                    149:                                break;
                    150:                        case 't':
                    151:                                smart_str_appendc(&retval, '\t');
                    152:                                prompt_spec++;
                    153:                                break;
                    154:                        case 'e':
                    155:                                smart_str_appendc(&retval, '\033');
                    156:                                prompt_spec++;
                    157:                                break;
                    158: 
                    159: 
                    160:                        case 'v':
                    161:                                smart_str_appends(&retval, PHP_VERSION);
                    162:                                prompt_spec++;
                    163:                                break;
                    164:                        case 'b':
                    165:                                smart_str_appends(&retval, block);
                    166:                                prompt_spec++;
                    167:                                break;
                    168:                        case '>':
                    169:                                smart_str_appendc(&retval, prompt);
                    170:                                prompt_spec++;
                    171:                                break;
                    172:                        case '`':
                    173:                                smart_str_appendc(&retval, '`');
                    174:                                prompt_spec++;
                    175:                                break;
                    176:                        default:
                    177:                                smart_str_appendc(&retval, '\\');
                    178:                                break;
                    179:                        }
                    180:                } else if (*prompt_spec == '`') {
                    181:                        char *prompt_end = strstr(prompt_spec + 1, "`");
                    182:                        char *code;
                    183: 
                    184:                        if (prompt_end) {
                    185:                                code = estrndup(prompt_spec + 1, prompt_end - prompt_spec - 1);
                    186: 
                    187:                                CLIR_G(prompt_str) = &retval;
                    188:                                zend_try {
                    189:                                        zend_eval_stringl(code, prompt_end - prompt_spec - 1, NULL, "php prompt code" TSRMLS_CC);
                    190:                                } zend_end_try();
                    191:                                CLIR_G(prompt_str) = NULL;
                    192:                                efree(code);
                    193:                                prompt_spec = prompt_end;
                    194:                        }
                    195:                } else {
                    196:                        smart_str_appendc(&retval, *prompt_spec);
                    197:                }
                    198:        } while (++prompt_spec && *prompt_spec);
                    199:        smart_str_0(&retval);   
                    200:        return retval.c;
                    201: }
                    202: /* }}} */
                    203: 
                    204: static int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */
                    205: {
                    206:        int valid_end = 1, last_valid_end;
                    207:        int brackets_count = 0;
                    208:        int brace_count = 0;
                    209:        int i;
                    210:        php_code_type code_type = body;
                    211:        char *heredoc_tag;
                    212:        int heredoc_len;
                    213: 
                    214:        for (i = 0; i < len; ++i) {
                    215:                switch(code_type) {
                    216:                        default:
                    217:                                switch(code[i]) {
                    218:                                        case '{':
                    219:                                                brackets_count++;
                    220:                                                valid_end = 0;
                    221:                                                break;
                    222:                                        case '}':
                    223:                                                if (brackets_count > 0) {
                    224:                                                        brackets_count--;
                    225:                                                }
                    226:                                                valid_end = brackets_count ? 0 : 1;
                    227:                                                break;
                    228:                                        case '(':
                    229:                                                brace_count++;
                    230:                                                valid_end = 0;
                    231:                                                break;
                    232:                                        case ')':
                    233:                                                if (brace_count > 0) {
                    234:                                                        brace_count--;
                    235:                                                }
                    236:                                                valid_end = 0;
                    237:                                                break;
                    238:                                        case ';':
                    239:                                                valid_end = brace_count == 0 && brackets_count == 0;
                    240:                                                break;
                    241:                                        case ' ':
                    242:                                        case '\r':
                    243:                                        case '\n':
                    244:                                        case '\t':
                    245:                                                break;
                    246:                                        case '\'':
                    247:                                                code_type = sstring;
                    248:                                                break;
                    249:                                        case '"':
                    250:                                                code_type = dstring;
                    251:                                                break;
                    252:                                        case '#':
                    253:                                                code_type = comment_line;
                    254:                                                break;
                    255:                                        case '/':
                    256:                                                if (code[i+1] == '/') {
                    257:                                                        i++;
                    258:                                                        code_type = comment_line;
                    259:                                                        break;
                    260:                                                }
                    261:                                                if (code[i+1] == '*') {
                    262:                                                        last_valid_end = valid_end;
                    263:                                                        valid_end = 0;
                    264:                                                        code_type = comment_block;
                    265:                                                        i++;
                    266:                                                        break;
                    267:                                                }
                    268:                                                valid_end = 0;
                    269:                                                break;
                    270:                                        case '%':
                    271:                                                if (!CG(asp_tags)) {
                    272:                                                        valid_end = 0;
                    273:                                                        break;
                    274:                                                }
                    275:                                                /* no break */
                    276:                                        case '?':
                    277:                                                if (code[i+1] == '>') {
                    278:                                                        i++;
                    279:                                                        code_type = outside;
                    280:                                                        break;
                    281:                                                }
                    282:                                                valid_end = 0;
                    283:                                                break;
                    284:                                        case '<':
                    285:                                                valid_end = 0;
                    286:                                                if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
                    287:                                                        i += 2;
                    288:                                                        code_type = heredoc_start;
                    289:                                                        heredoc_len = 0;
                    290:                                                }
                    291:                                                break;
                    292:                                        default:
                    293:                                                valid_end = 0;
                    294:                                                break;
                    295:                                }
                    296:                                break;
                    297:                        case sstring:
                    298:                                if (code[i] == '\\') {
                    299:                                        code_type = sstring_esc;
                    300:                                } else {
                    301:                                        if (code[i] == '\'') {
                    302:                                                code_type = body;
                    303:                                        }
                    304:                                }
                    305:                                break;
                    306:                        case sstring_esc:
                    307:                                code_type = sstring;
                    308:                                break;
                    309:                        case dstring:
                    310:                                if (code[i] == '\\') {
                    311:                                        code_type = dstring_esc;
                    312:                                } else {
                    313:                                        if (code[i] == '"') {
                    314:                                                code_type = body;
                    315:                                        }
                    316:                                }
                    317:                                break;
                    318:                        case dstring_esc:
                    319:                                code_type = dstring;
                    320:                                break;
                    321:                        case comment_line:
                    322:                                if (code[i] == '\n') {
                    323:                                        code_type = body;
                    324:                                }
                    325:                                break;
                    326:                        case comment_block:
                    327:                                if (code[i-1] == '*' && code[i] == '/') {
                    328:                                        code_type = body;
                    329:                                        valid_end = last_valid_end;
                    330:                                }
                    331:                                break;
                    332:                        case heredoc_start:
                    333:                                switch(code[i]) {
                    334:                                        case ' ':
                    335:                                        case '\t':
                    336:                                        case '\'':
                    337:                                                break;
                    338:                                        case '\r':
                    339:                                        case '\n':
                    340:                                                code_type = heredoc;
                    341:                                                break;
                    342:                                        default:
                    343:                                                if (!heredoc_len) {
                    344:                                                        heredoc_tag = code+i;
                    345:                                                }
                    346:                                                heredoc_len++;
                    347:                                                break;
                    348:                                }
                    349:                                break;
                    350:                        case heredoc:
                    351:                                if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {
                    352:                                        code_type = body;
                    353:                                } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') {
                    354:                                        code_type = body;
                    355:                                        valid_end = 1;
                    356:                                }
                    357:                                break;
                    358:                        case outside:
                    359:                                if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
                    360:                                ||  (CG(asp_tags) && !strncmp(code+i-1, "<%", 2))
                    361:                                ||  (i > 3 && !strncmp(code+i-4, "<?php", 5))
                    362:                                ) {
                    363:                                        code_type = body;
                    364:                                }
                    365:                                break;
                    366:                }
                    367:        }
                    368: 
                    369:        switch (code_type) {
                    370:                default:
                    371:                        if (brace_count) {
                    372:                                *prompt = cli_get_prompt("php", '(' TSRMLS_CC);
                    373:                        } else if (brackets_count) {
                    374:                                *prompt = cli_get_prompt("php", '{' TSRMLS_CC);
                    375:                        } else {
                    376:                                *prompt = cli_get_prompt("php", '>' TSRMLS_CC);
                    377:                        }
                    378:                        break;
                    379:                case sstring:
                    380:                case sstring_esc:
                    381:                        *prompt = cli_get_prompt("php", '\'' TSRMLS_CC);
                    382:                        break;
                    383:                case dstring:
                    384:                case dstring_esc:
                    385:                        *prompt = cli_get_prompt("php", '"' TSRMLS_CC);
                    386:                        break;
                    387:                case comment_block:
                    388:                        *prompt = cli_get_prompt("/* ", '>' TSRMLS_CC);
                    389:                        break;
                    390:                case heredoc:
                    391:                        *prompt = cli_get_prompt("<<<", '>' TSRMLS_CC);
                    392:                        break;
                    393:                case outside:
                    394:                        *prompt = cli_get_prompt("   ", '>' TSRMLS_CC);
                    395:                        break;
                    396:        }
                    397: 
                    398:        if (!valid_end || brackets_count) {
                    399:                return 0;
                    400:        } else {
                    401:                return 1;
                    402:        }
                    403: }
                    404: /* }}} */
                    405: 
                    406: static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */
                    407: {
                    408:        char *name;
                    409:        ulong number;
                    410: 
                    411:        if (!(*state % 2)) {
                    412:                zend_hash_internal_pointer_reset(ht);
                    413:                (*state)++;
                    414:        }
                    415:        while(zend_hash_has_more_elements(ht) == SUCCESS) {
                    416:                zend_hash_get_current_key(ht, &name, &number, 0);
                    417:                if (!textlen || !strncmp(name, text, textlen)) {
                    418:                        if (pData) {
                    419:                                zend_hash_get_current_data(ht, pData);
                    420:                        }
                    421:                        zend_hash_move_forward(ht);
                    422:                        return name;
                    423:                }
                    424:                if (zend_hash_move_forward(ht) == FAILURE) {
                    425:                        break;
                    426:                }
                    427:        }
                    428:        (*state)++;
                    429:        return NULL;
                    430: } /* }}} */
                    431: 
                    432: static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
                    433: {
                    434:        char *retval, *tmp;
                    435: 
                    436:        tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC);
                    437:        if (retval) {
                    438:                retval = malloc(strlen(tmp) + 2);
                    439:                retval[0] = '$';
                    440:                strcpy(&retval[1], tmp);
                    441:                rl_completion_append_character = '\0';
                    442:        }
                    443:        return retval;
                    444: } /* }}} */
                    445: 
                    446: static char *cli_completion_generator_ini(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
                    447: {
                    448:        char *retval, *tmp;
                    449: 
                    450:        tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(ini_directives), NULL TSRMLS_CC);
                    451:        if (retval) {
                    452:                retval = malloc(strlen(tmp) + 2);
                    453:                retval[0] = '#';
                    454:                strcpy(&retval[1], tmp);
                    455:                rl_completion_append_character = '=';
                    456:        }
                    457:        return retval;
                    458: } /* }}} */
                    459: 
                    460: static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
                    461: {
                    462:        zend_function *func;
                    463:        char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);
                    464:        if (retval) {
                    465:                rl_completion_append_character = '(';
                    466:                retval = strdup(func->common.function_name);
                    467:        }
                    468:        
                    469:        return retval;
                    470: } /* }}} */
                    471: 
                    472: static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
                    473: {
                    474:        zend_class_entry **pce;
                    475:        char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);
                    476:        if (retval) {
                    477:                rl_completion_append_character = '\0';
                    478:                retval = strdup((*pce)->name);
                    479:        }
                    480:        
                    481:        return retval;
                    482: } /* }}} */
                    483: 
                    484: static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
                    485: {
                    486:        zend_class_entry **pce;
                    487:        char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);
                    488:        if (retval) {
                    489:                rl_completion_append_character = '\0';
                    490:                retval = strdup(retval);
                    491:        }
                    492:        
                    493:        return retval;
                    494: } /* }}} */
                    495: 
                    496: static int cli_completion_state;
                    497: 
                    498: static char *cli_completion_generator(const char *text, int index) /* {{{ */
                    499: {
                    500: /*
                    501: TODO:
                    502: - constants
                    503: - maybe array keys
                    504: - language constructs and other things outside a hashtable (echo, try, function, class, ...)
                    505: - object/class members
                    506: 
                    507: - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
                    508: */
                    509:        char *retval = NULL;
                    510:        int textlen = strlen(text);
                    511:        TSRMLS_FETCH();
                    512: 
                    513:        if (!index) {
                    514:                cli_completion_state = 0;
                    515:        }
                    516:        if (text[0] == '$') {
                    517:                retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC);
                    518:        } else if (text[0] == '#') {
                    519:                retval = cli_completion_generator_ini(text, textlen, &cli_completion_state TSRMLS_CC);
                    520:        } else {
                    521:                char *lc_text, *class_name, *class_name_end;
                    522:                int class_name_len;
                    523:                zend_class_entry **pce = NULL;
                    524:                
                    525:                class_name_end = strstr(text, "::");
                    526:                if (class_name_end) {
                    527:                        class_name_len = class_name_end - text;
                    528:                        class_name = zend_str_tolower_dup(text, class_name_len);
                    529:                        class_name[class_name_len] = '\0'; /* not done automatically */
                    530:                        if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {
                    531:                                efree(class_name);
                    532:                                return NULL;
                    533:                        }
                    534:                        lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
                    535:                        textlen -= (class_name_len + 2);
                    536:                } else {
                    537:                        lc_text = zend_str_tolower_dup(text, textlen);
                    538:                }
                    539: 
                    540:                switch (cli_completion_state) {
                    541:                        case 0:
                    542:                        case 1:
                    543:                                retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);
                    544:                                if (retval) {
                    545:                                        break;
                    546:                                }
                    547:                        case 2:
                    548:                        case 3:
                    549:                                retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);
                    550:                                if (retval || pce) {
                    551:                                        break;
                    552:                                }
                    553:                        case 4:
                    554:                        case 5:
                    555:                                retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);
                    556:                                break;
                    557:                        default:
                    558:                                break;
                    559:                }
                    560:                efree(lc_text);
                    561:                if (class_name_end) {
                    562:                        efree(class_name);
                    563:                }
                    564:                if (pce && retval) {
                    565:                        int len = class_name_len + 2 + strlen(retval) + 1;
                    566:                        char *tmp = malloc(len);
                    567:                        
                    568:                        snprintf(tmp, len, "%s::%s", (*pce)->name, retval);
                    569:                        free(retval);
                    570:                        retval = tmp;
                    571:                }
                    572:        }
                    573:        
                    574:        return retval;
                    575: } /* }}} */
                    576: 
                    577: static char **cli_code_completion(const char *text, int start, int end) /* {{{ */
                    578: {
                    579:        return rl_completion_matches(text, cli_completion_generator);
                    580: }
                    581: /* }}} */
                    582: 
                    583: static int readline_shell_run(TSRMLS_D) /* {{{ */
                    584: {
                    585:        char *line;
                    586:        size_t size = 4096, pos = 0, len;
                    587:        char *code = emalloc(size);
                    588:        char *prompt = cli_get_prompt("php", '>' TSRMLS_CC);
                    589:        char *history_file;
                    590: 
                    591:        if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) {
                    592:                zend_file_handle *prepend_file_p;
                    593:                zend_file_handle prepend_file = {0};
                    594: 
                    595:                prepend_file.filename = PG(auto_prepend_file);
                    596:                prepend_file.opened_path = NULL;
                    597:                prepend_file.free_filename = 0;
                    598:                prepend_file.type = ZEND_HANDLE_FILENAME;
                    599:                prepend_file_p = &prepend_file;
                    600: 
                    601:                zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 1, prepend_file_p);
                    602:        }
                    603: 
                    604:        history_file = tilde_expand("~/.php_history");
                    605:        rl_attempted_completion_function = cli_code_completion;
                    606:        rl_special_prefixes = "$";
                    607:        read_history(history_file);
                    608: 
                    609:        EG(exit_status) = 0;
                    610:        while ((line = readline(prompt)) != NULL) {
                    611:                if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) {
                    612:                        free(line);
                    613:                        break;
                    614:                }
                    615: 
                    616:                if (!pos && !*line) {
                    617:                        free(line);
                    618:                        continue;
                    619:                }
                    620: 
                    621:                len = strlen(line);
                    622: 
                    623:                if (line[0] == '#') {
                    624:                        char *param = strstr(&line[1], "=");
                    625:                        if (param) {
                    626:                                char *cmd;
                    627:                                uint cmd_len;
                    628:                                param++;
                    629:                                cmd_len = param - &line[1] - 1;
                    630:                                cmd = estrndup(&line[1], cmd_len);
                    631: 
                    632:                                zend_alter_ini_entry_ex(cmd, cmd_len + 1, param, strlen(param), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC);
                    633:                                efree(cmd);
                    634:                                add_history(line);
                    635: 
                    636:                                efree(prompt);
                    637:                                /* TODO: This might be wrong! */
                    638:                                prompt = cli_get_prompt("php", '>' TSRMLS_CC);
                    639:                                continue;
                    640:                        }
                    641:                }
                    642: 
                    643:                if (pos + len + 2 > size) {
                    644:                        size = pos + len + 2;
                    645:                        code = erealloc(code, size);
                    646:                }
                    647:                memcpy(&code[pos], line, len);
                    648:                pos += len;
                    649:                code[pos] = '\n';
                    650:                code[++pos] = '\0';
                    651: 
                    652:                if (*line) {
                    653:                        add_history(line);
                    654:                }
                    655: 
                    656:                free(line);
                    657:                efree(prompt);
                    658: 
                    659:                if (!cli_is_valid_code(code, pos, &prompt TSRMLS_CC)) {
                    660:                        continue;
                    661:                }
                    662: 
                    663:                zend_try {
                    664:                        zend_eval_stringl(code, pos, NULL, "php shell code" TSRMLS_CC);
                    665:                } zend_end_try();
                    666: 
                    667:                pos = 0;
                    668:                                        
                    669:                if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') {
                    670:                        readline_shell_write("\n", 1 TSRMLS_CC);
                    671:                }
                    672: 
                    673:                if (EG(exception)) {
                    674:                        zend_exception_error(EG(exception), E_WARNING TSRMLS_CC);
                    675:                }
                    676: 
                    677:                if (pager_pipe) {
                    678:                        fclose(pager_pipe);
                    679:                        pager_pipe = NULL;
                    680:                }
                    681: 
                    682:                php_last_char = '\0';
                    683:        }
                    684:        write_history(history_file);
                    685:        free(history_file);
                    686:        efree(code);
                    687:        efree(prompt);
                    688:        return EG(exit_status);
                    689: }
                    690: /* }}} */
                    691: 
                    692: /*
                    693: #ifdef COMPILE_DL_READLINE
                    694: This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only
                    695: extensions. If that is being changed dlsym() should only be used when building
                    696: this extension sharedto offer compatibility.
                    697: */
                    698: #define GET_SHELL_CB(cb) \
                    699:        do { \
                    700:                (cb) = NULL; \
                    701:                cli_shell_callbacks_t *(*get_callbacks)(); \
                    702:                get_callbacks = dlsym(RTLD_DEFAULT, "php_cli_get_shell_callbacks"); \
                    703:                if (get_callbacks) { \
                    704:                        (cb) = get_callbacks(); \
                    705:                } \
                    706:        } while(0)
                    707: /*#else
                    708: #define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks()
                    709: #endif*/
                    710: 
                    711: PHP_MINIT_FUNCTION(cli_readline)
                    712: {
                    713:        cli_shell_callbacks_t *cb;
                    714: 
                    715:        ZEND_INIT_MODULE_GLOBALS(cli_readline, cli_readline_init_globals, NULL);
                    716:        REGISTER_INI_ENTRIES();
                    717: 
                    718: #if HAVE_LIBEDIT
                    719:        REGISTER_STRING_CONSTANT("READLINE_LIB", "libedit", CONST_CS|CONST_PERSISTENT);
                    720: #else
                    721:        REGISTER_STRING_CONSTANT("READLINE_LIB", "readline", CONST_CS|CONST_PERSISTENT);
                    722: #endif
                    723: 
                    724:        GET_SHELL_CB(cb);
                    725:        if (cb) {
                    726:                cb->cli_shell_write = readline_shell_write;
                    727:                cb->cli_shell_ub_write = readline_shell_ub_write;
                    728:                cb->cli_shell_run = readline_shell_run;
                    729:        }
                    730: 
                    731:        return SUCCESS;
                    732: }
                    733: 
                    734: PHP_MSHUTDOWN_FUNCTION(cli_readline)
                    735: {
                    736:        cli_shell_callbacks_t *cb;
                    737: 
                    738:        UNREGISTER_INI_ENTRIES();
                    739: 
                    740:        GET_SHELL_CB(cb);
                    741:        if (cb) {
                    742:                cb->cli_shell_write = NULL;
                    743:                cb->cli_shell_ub_write = NULL;
                    744:                cb->cli_shell_run = NULL;
                    745:        }
                    746: 
                    747:        return SUCCESS;
                    748: }
                    749: 
                    750: PHP_MINFO_FUNCTION(cli_readline)
                    751: {
                    752:        php_info_print_table_start();
                    753:        php_info_print_table_header(2, "Readline Support", "enabled");
                    754:        php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown"));
                    755:        php_info_print_table_end();
                    756: 
                    757:        DISPLAY_INI_ENTRIES();
                    758: }
                    759: 
                    760: /*
                    761:  * Local variables:
                    762:  * tab-width: 4
                    763:  * c-basic-offset: 4
                    764:  * End:
                    765:  * vim600: sw=4 ts=4 fdm=marker
                    766:  * vim<600: sw=4 ts=4
                    767:  */

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