Annotation of embedaddon/php/ext/readline/readline_cli.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$ */
        !            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>