You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

508 lines
12 KiB

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