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.

446 lines
10 KiB

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