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.

502 lines
12 KiB

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