Annotation of embedaddon/php/sapi/cli/php_cli_readline.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 The PHP Group |
! 6: +----------------------------------------------------------------------+
! 7: | This source file is subject to version 3.01 of the PHP license, |
! 8: | that is bundled with this package in the file LICENSE, and is |
! 9: | available through the world-wide-web at the following url: |
! 10: | http://www.php.net/license/3_01.txt |
! 11: | If you did not receive a copy of the PHP license and are unable to |
! 12: | obtain it through the world-wide-web, please send a note to |
! 13: | license@php.net so we can mail you a copy immediately. |
! 14: +----------------------------------------------------------------------+
! 15: | Author: Marcus Boerger <helly@php.net> |
! 16: | Johannes Schlueter <johannes@php.net> |
! 17: +----------------------------------------------------------------------+
! 18: */
! 19:
! 20: /* $Id: php_cli_readline.c 321634 2012-01-01 13:15:04Z felipe $ */
! 21:
! 22: #include "php.h"
! 23:
! 24: #if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE)
! 25:
! 26: #ifndef HAVE_RL_COMPLETION_MATCHES
! 27: #define rl_completion_matches completion_matches
! 28: #endif
! 29:
! 30: #include "php_globals.h"
! 31: #include "php_variables.h"
! 32: #include "zend_hash.h"
! 33: #include "zend_modules.h"
! 34:
! 35: #include "SAPI.h"
! 36:
! 37: #if HAVE_SETLOCALE
! 38: #include <locale.h>
! 39: #endif
! 40: #include "zend.h"
! 41: #include "zend_extensions.h"
! 42: #include "php_ini.h"
! 43: #include "php_globals.h"
! 44: #include "php_main.h"
! 45: #include "fopen_wrappers.h"
! 46: #include "ext/standard/php_standard.h"
! 47:
! 48: #ifdef __riscos__
! 49: #include <unixlib/local.h>
! 50: #endif
! 51:
! 52: #if HAVE_LIBEDIT
! 53: #include <editline/readline.h>
! 54: #else
! 55: #include <readline/readline.h>
! 56: #include <readline/history.h>
! 57: #endif
! 58:
! 59: #include "zend_compile.h"
! 60: #include "zend_execute.h"
! 61: #include "zend_highlight.h"
! 62: #include "zend_indent.h"
! 63:
! 64: typedef enum {
! 65: body,
! 66: sstring,
! 67: dstring,
! 68: sstring_esc,
! 69: dstring_esc,
! 70: comment_line,
! 71: comment_block,
! 72: heredoc_start,
! 73: heredoc,
! 74: outside,
! 75: } php_code_type;
! 76:
! 77: int cli_is_valid_code(char *code, int len, char **prompt TSRMLS_DC) /* {{{ */
! 78: {
! 79: int valid_end = 1, last_valid_end;
! 80: int brackets_count = 0;
! 81: int brace_count = 0;
! 82: int i;
! 83: php_code_type code_type = body;
! 84: char *heredoc_tag;
! 85: int heredoc_len;
! 86:
! 87: for (i = 0; i < len; ++i) {
! 88: switch(code_type) {
! 89: default:
! 90: switch(code[i]) {
! 91: case '{':
! 92: brackets_count++;
! 93: valid_end = 0;
! 94: break;
! 95: case '}':
! 96: if (brackets_count > 0) {
! 97: brackets_count--;
! 98: }
! 99: valid_end = brackets_count ? 0 : 1;
! 100: break;
! 101: case '(':
! 102: brace_count++;
! 103: valid_end = 0;
! 104: break;
! 105: case ')':
! 106: if (brace_count > 0) {
! 107: brace_count--;
! 108: }
! 109: valid_end = 0;
! 110: break;
! 111: case ';':
! 112: valid_end = brace_count == 0 && brackets_count == 0;
! 113: break;
! 114: case ' ':
! 115: case '\r':
! 116: case '\n':
! 117: case '\t':
! 118: break;
! 119: case '\'':
! 120: code_type = sstring;
! 121: break;
! 122: case '"':
! 123: code_type = dstring;
! 124: break;
! 125: case '#':
! 126: code_type = comment_line;
! 127: break;
! 128: case '/':
! 129: if (code[i+1] == '/') {
! 130: i++;
! 131: code_type = comment_line;
! 132: break;
! 133: }
! 134: if (code[i+1] == '*') {
! 135: last_valid_end = valid_end;
! 136: valid_end = 0;
! 137: code_type = comment_block;
! 138: i++;
! 139: break;
! 140: }
! 141: valid_end = 0;
! 142: break;
! 143: case '%':
! 144: if (!CG(asp_tags)) {
! 145: valid_end = 0;
! 146: break;
! 147: }
! 148: /* no break */
! 149: case '?':
! 150: if (code[i+1] == '>') {
! 151: i++;
! 152: code_type = outside;
! 153: break;
! 154: }
! 155: valid_end = 0;
! 156: break;
! 157: case '<':
! 158: valid_end = 0;
! 159: if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
! 160: i += 2;
! 161: code_type = heredoc_start;
! 162: heredoc_len = 0;
! 163: }
! 164: break;
! 165: default:
! 166: valid_end = 0;
! 167: break;
! 168: }
! 169: break;
! 170: case sstring:
! 171: if (code[i] == '\\') {
! 172: code_type = sstring_esc;
! 173: } else {
! 174: if (code[i] == '\'') {
! 175: code_type = body;
! 176: }
! 177: }
! 178: break;
! 179: case sstring_esc:
! 180: code_type = sstring;
! 181: break;
! 182: case dstring:
! 183: if (code[i] == '\\') {
! 184: code_type = dstring_esc;
! 185: } else {
! 186: if (code[i] == '"') {
! 187: code_type = body;
! 188: }
! 189: }
! 190: break;
! 191: case dstring_esc:
! 192: code_type = dstring;
! 193: break;
! 194: case comment_line:
! 195: if (code[i] == '\n') {
! 196: code_type = body;
! 197: }
! 198: break;
! 199: case comment_block:
! 200: if (code[i-1] == '*' && code[i] == '/') {
! 201: code_type = body;
! 202: valid_end = last_valid_end;
! 203: }
! 204: break;
! 205: case heredoc_start:
! 206: switch(code[i]) {
! 207: case ' ':
! 208: case '\t':
! 209: break;
! 210: case '\r':
! 211: case '\n':
! 212: code_type = heredoc;
! 213: break;
! 214: default:
! 215: if (!heredoc_len) {
! 216: heredoc_tag = code+i;
! 217: }
! 218: heredoc_len++;
! 219: break;
! 220: }
! 221: break;
! 222: case heredoc:
! 223: if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {
! 224: code_type = body;
! 225: } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') {
! 226: code_type = body;
! 227: valid_end = 1;
! 228: }
! 229: break;
! 230: case outside:
! 231: if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
! 232: || (CG(asp_tags) && !strncmp(code+i-1, "<%", 2))
! 233: || (i > 3 && !strncmp(code+i-4, "<?php", 5))
! 234: ) {
! 235: code_type = body;
! 236: }
! 237: break;
! 238: }
! 239: }
! 240:
! 241: switch (code_type) {
! 242: default:
! 243: if (brace_count) {
! 244: *prompt = "php ( ";
! 245: } else if (brackets_count) {
! 246: *prompt = "php { ";
! 247: } else {
! 248: *prompt = "php > ";
! 249: }
! 250: break;
! 251: case sstring:
! 252: case sstring_esc:
! 253: *prompt = "php ' ";
! 254: break;
! 255: case dstring:
! 256: case dstring_esc:
! 257: *prompt = "php \" ";
! 258: break;
! 259: case comment_block:
! 260: *prompt = "/* > ";
! 261: break;
! 262: case heredoc:
! 263: *prompt = "<<< > ";
! 264: break;
! 265: case outside:
! 266: *prompt = " > ";
! 267: break;
! 268: }
! 269:
! 270: if (!valid_end || brackets_count) {
! 271: return 0;
! 272: } else {
! 273: return 1;
! 274: }
! 275: }
! 276: /* }}} */
! 277:
! 278: static char *cli_completion_generator_ht(const char *text, int textlen, int *state, HashTable *ht, void **pData TSRMLS_DC) /* {{{ */
! 279: {
! 280: char *name;
! 281: ulong number;
! 282:
! 283: if (!(*state % 2)) {
! 284: zend_hash_internal_pointer_reset(ht);
! 285: (*state)++;
! 286: }
! 287: while(zend_hash_has_more_elements(ht) == SUCCESS) {
! 288: zend_hash_get_current_key(ht, &name, &number, 0);
! 289: if (!textlen || !strncmp(name, text, textlen)) {
! 290: if (pData) {
! 291: zend_hash_get_current_data(ht, pData);
! 292: }
! 293: zend_hash_move_forward(ht);
! 294: return name;
! 295: }
! 296: if (zend_hash_move_forward(ht) == FAILURE) {
! 297: break;
! 298: }
! 299: }
! 300: (*state)++;
! 301: return NULL;
! 302: } /* }}} */
! 303:
! 304: static char *cli_completion_generator_var(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
! 305: {
! 306: char *retval, *tmp;
! 307:
! 308: tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(active_symbol_table), NULL TSRMLS_CC);
! 309: if (retval) {
! 310: retval = malloc(strlen(tmp) + 2);
! 311: retval[0] = '$';
! 312: strcpy(&retval[1], tmp);
! 313: rl_completion_append_character = '\0';
! 314: }
! 315: return retval;
! 316: } /* }}} */
! 317:
! 318: static char *cli_completion_generator_func(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
! 319: {
! 320: zend_function *func;
! 321: char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func TSRMLS_CC);
! 322: if (retval) {
! 323: rl_completion_append_character = '(';
! 324: retval = strdup(func->common.function_name);
! 325: }
! 326:
! 327: return retval;
! 328: } /* }}} */
! 329:
! 330: static char *cli_completion_generator_class(const char *text, int textlen, int *state TSRMLS_DC) /* {{{ */
! 331: {
! 332: zend_class_entry **pce;
! 333: char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&pce TSRMLS_CC);
! 334: if (retval) {
! 335: rl_completion_append_character = '\0';
! 336: retval = strdup((*pce)->name);
! 337: }
! 338:
! 339: return retval;
! 340: } /* }}} */
! 341:
! 342: static char *cli_completion_generator_define(const char *text, int textlen, int *state, HashTable *ht TSRMLS_DC) /* {{{ */
! 343: {
! 344: zend_class_entry **pce;
! 345: char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce TSRMLS_CC);
! 346: if (retval) {
! 347: rl_completion_append_character = '\0';
! 348: retval = strdup(retval);
! 349: }
! 350:
! 351: return retval;
! 352: } /* }}} */
! 353:
! 354: static int cli_completion_state;
! 355:
! 356: static char *cli_completion_generator(const char *text, int index) /* {{{ */
! 357: {
! 358: /*
! 359: TODO:
! 360: - constants
! 361: - maybe array keys
! 362: - language constructs and other things outside a hashtable (echo, try, function, class, ...)
! 363: - object/class members
! 364:
! 365: - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
! 366: */
! 367: char *retval = NULL;
! 368: int textlen = strlen(text);
! 369: TSRMLS_FETCH();
! 370:
! 371: if (!index) {
! 372: cli_completion_state = 0;
! 373: }
! 374: if (text[0] == '$') {
! 375: retval = cli_completion_generator_var(text, textlen, &cli_completion_state TSRMLS_CC);
! 376: } else {
! 377: char *lc_text, *class_name, *class_name_end;
! 378: int class_name_len;
! 379: zend_class_entry **pce = NULL;
! 380:
! 381: class_name_end = strstr(text, "::");
! 382: if (class_name_end) {
! 383: class_name_len = class_name_end - text;
! 384: class_name = zend_str_tolower_dup(text, class_name_len);
! 385: class_name[class_name_len] = '\0'; /* not done automatically */
! 386: if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC)==FAILURE) {
! 387: efree(class_name);
! 388: return NULL;
! 389: }
! 390: lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
! 391: textlen -= (class_name_len + 2);
! 392: } else {
! 393: lc_text = zend_str_tolower_dup(text, textlen);
! 394: }
! 395:
! 396: switch (cli_completion_state) {
! 397: case 0:
! 398: case 1:
! 399: retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, pce ? &(*pce)->function_table : EG(function_table) TSRMLS_CC);
! 400: if (retval) {
! 401: break;
! 402: }
! 403: case 2:
! 404: case 3:
! 405: retval = cli_completion_generator_define(text, textlen, &cli_completion_state, pce ? &(*pce)->constants_table : EG(zend_constants) TSRMLS_CC);
! 406: if (retval || pce) {
! 407: break;
! 408: }
! 409: case 4:
! 410: case 5:
! 411: retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state TSRMLS_CC);
! 412: break;
! 413: default:
! 414: break;
! 415: }
! 416: efree(lc_text);
! 417: if (class_name_end) {
! 418: efree(class_name);
! 419: }
! 420: if (pce && retval) {
! 421: int len = class_name_len + 2 + strlen(retval) + 1;
! 422: char *tmp = malloc(len);
! 423:
! 424: snprintf(tmp, len, "%s::%s", (*pce)->name, retval);
! 425: free(retval);
! 426: retval = tmp;
! 427: }
! 428: }
! 429:
! 430: return retval;
! 431: } /* }}} */
! 432:
! 433: char **cli_code_completion(const char *text, int start, int end) /* {{{ */
! 434: {
! 435: return rl_completion_matches(text, cli_completion_generator);
! 436: }
! 437: /* }}} */
! 438:
! 439: #endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */
! 440:
! 441: /*
! 442: * Local variables:
! 443: * tab-width: 4
! 444: * c-basic-offset: 4
! 445: * End:
! 446: * vim600: sw=4 ts=4 fdm=marker
! 447: * vim<600: sw=4 ts=4
! 448: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>