readline_cli.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Marcus Boerger <helly@php.net> |
  14. | Johannes Schlueter <johannes@php.net> |
  15. +----------------------------------------------------------------------+
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. #include "config.h"
  19. #endif
  20. #include "php.h"
  21. #ifndef HAVE_RL_COMPLETION_MATCHES
  22. #define rl_completion_matches completion_matches
  23. #endif
  24. #include "php_globals.h"
  25. #include "php_variables.h"
  26. #include "zend_hash.h"
  27. #include "zend_modules.h"
  28. #include "SAPI.h"
  29. #include <locale.h>
  30. #include "zend.h"
  31. #include "zend_extensions.h"
  32. #include "php_ini.h"
  33. #include "php_globals.h"
  34. #include "php_main.h"
  35. #include "fopen_wrappers.h"
  36. #include "ext/standard/php_standard.h"
  37. #include "zend_smart_str.h"
  38. #ifdef __riscos__
  39. #include <unixlib/local.h>
  40. #endif
  41. #if HAVE_LIBEDIT
  42. #include <editline/readline.h>
  43. #else
  44. #include <readline/readline.h>
  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_exceptions.h"
  51. #include "sapi/cli/cli.h"
  52. #include "readline_cli.h"
  53. #if defined(COMPILE_DL_READLINE) && !defined(PHP_WIN32)
  54. #include <dlfcn.h>
  55. #endif
  56. #ifndef RTLD_DEFAULT
  57. #define RTLD_DEFAULT NULL
  58. #endif
  59. #define DEFAULT_PROMPT "\\b \\> "
  60. ZEND_DECLARE_MODULE_GLOBALS(cli_readline)
  61. static char php_last_char = '\0';
  62. static FILE *pager_pipe = NULL;
  63. static size_t readline_shell_write(const char *str, size_t str_length) /* {{{ */
  64. {
  65. if (CLIR_G(prompt_str)) {
  66. smart_str_appendl(CLIR_G(prompt_str), str, str_length);
  67. return str_length;
  68. }
  69. if (CLIR_G(pager) && *CLIR_G(pager) && !pager_pipe) {
  70. pager_pipe = VCWD_POPEN(CLIR_G(pager), "w");
  71. }
  72. if (pager_pipe) {
  73. return fwrite(str, 1, MIN(str_length, 16384), pager_pipe);
  74. }
  75. return -1;
  76. }
  77. /* }}} */
  78. static size_t readline_shell_ub_write(const char *str, size_t str_length) /* {{{ */
  79. {
  80. /* We just store the last char here and then pass back to the
  81. caller (sapi_cli_single_write in sapi/cli) which will actually
  82. write due to -1 return code */
  83. php_last_char = str[str_length-1];
  84. return (size_t) -1;
  85. }
  86. /* }}} */
  87. static void cli_readline_init_globals(zend_cli_readline_globals *rg)
  88. {
  89. rg->pager = NULL;
  90. rg->prompt = NULL;
  91. rg->prompt_str = NULL;
  92. }
  93. PHP_INI_BEGIN()
  94. STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals)
  95. STD_PHP_INI_ENTRY("cli.prompt", DEFAULT_PROMPT, PHP_INI_ALL, OnUpdateString, prompt, zend_cli_readline_globals, cli_readline_globals)
  96. PHP_INI_END()
  97. typedef enum {
  98. body,
  99. sstring,
  100. dstring,
  101. sstring_esc,
  102. dstring_esc,
  103. comment_line,
  104. comment_block,
  105. heredoc_start,
  106. heredoc,
  107. outside,
  108. } php_code_type;
  109. static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */
  110. {
  111. smart_str retval = {0};
  112. char *prompt_spec = CLIR_G(prompt) ? CLIR_G(prompt) : DEFAULT_PROMPT;
  113. bool unicode_warned = false;
  114. do {
  115. if (*prompt_spec == '\\') {
  116. switch (prompt_spec[1]) {
  117. case '\\':
  118. smart_str_appendc(&retval, '\\');
  119. prompt_spec++;
  120. break;
  121. case 'n':
  122. smart_str_appendc(&retval, '\n');
  123. prompt_spec++;
  124. break;
  125. case 't':
  126. smart_str_appendc(&retval, '\t');
  127. prompt_spec++;
  128. break;
  129. case 'e':
  130. smart_str_appendc(&retval, '\033');
  131. prompt_spec++;
  132. break;
  133. case 'v':
  134. smart_str_appends(&retval, PHP_VERSION);
  135. prompt_spec++;
  136. break;
  137. case 'b':
  138. smart_str_appends(&retval, block);
  139. prompt_spec++;
  140. break;
  141. case '>':
  142. smart_str_appendc(&retval, prompt);
  143. prompt_spec++;
  144. break;
  145. case '`':
  146. smart_str_appendc(&retval, '`');
  147. prompt_spec++;
  148. break;
  149. default:
  150. smart_str_appendc(&retval, '\\');
  151. break;
  152. }
  153. } else if (*prompt_spec == '`') {
  154. char *prompt_end = strstr(prompt_spec + 1, "`");
  155. char *code;
  156. if (prompt_end) {
  157. code = estrndup(prompt_spec + 1, prompt_end - prompt_spec - 1);
  158. CLIR_G(prompt_str) = &retval;
  159. zend_try {
  160. zend_eval_stringl(code, prompt_end - prompt_spec - 1, NULL, "php prompt code");
  161. } zend_end_try();
  162. CLIR_G(prompt_str) = NULL;
  163. efree(code);
  164. prompt_spec = prompt_end;
  165. }
  166. } else {
  167. if (!(*prompt_spec & 0x80)) {
  168. smart_str_appendc(&retval, *prompt_spec);
  169. } else {
  170. if (!unicode_warned) {
  171. zend_error(E_WARNING,
  172. "prompt contains unsupported unicode characters");
  173. unicode_warned = true;
  174. }
  175. smart_str_appendc(&retval, '?');
  176. }
  177. }
  178. } while (++prompt_spec && *prompt_spec);
  179. smart_str_0(&retval);
  180. return retval.s;
  181. }
  182. /* }}} */
  183. static int cli_is_valid_code(char *code, size_t len, zend_string **prompt) /* {{{ */
  184. {
  185. int valid_end = 1, last_valid_end;
  186. int brackets_count = 0;
  187. int brace_count = 0;
  188. size_t i;
  189. php_code_type code_type = body;
  190. char *heredoc_tag = NULL;
  191. size_t heredoc_len;
  192. for (i = 0; i < len; ++i) {
  193. switch(code_type) {
  194. default:
  195. switch(code[i]) {
  196. case '{':
  197. brackets_count++;
  198. valid_end = 0;
  199. break;
  200. case '}':
  201. if (brackets_count > 0) {
  202. brackets_count--;
  203. }
  204. valid_end = brackets_count ? 0 : 1;
  205. break;
  206. case '(':
  207. brace_count++;
  208. valid_end = 0;
  209. break;
  210. case ')':
  211. if (brace_count > 0) {
  212. brace_count--;
  213. }
  214. valid_end = 0;
  215. break;
  216. case ';':
  217. valid_end = brace_count == 0 && brackets_count == 0;
  218. break;
  219. case ' ':
  220. case '\r':
  221. case '\n':
  222. case '\t':
  223. break;
  224. case '\'':
  225. code_type = sstring;
  226. break;
  227. case '"':
  228. code_type = dstring;
  229. break;
  230. case '#':
  231. if (code[i+1] == '[') {
  232. valid_end = 0;
  233. break;
  234. }
  235. code_type = comment_line;
  236. break;
  237. case '/':
  238. if (code[i+1] == '/') {
  239. i++;
  240. code_type = comment_line;
  241. break;
  242. }
  243. if (code[i+1] == '*') {
  244. last_valid_end = valid_end;
  245. valid_end = 0;
  246. code_type = comment_block;
  247. i++;
  248. break;
  249. }
  250. valid_end = 0;
  251. break;
  252. case '?':
  253. if (code[i+1] == '>') {
  254. i++;
  255. code_type = outside;
  256. break;
  257. }
  258. valid_end = 0;
  259. break;
  260. case '<':
  261. valid_end = 0;
  262. if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
  263. i += 2;
  264. code_type = heredoc_start;
  265. heredoc_tag = NULL;
  266. heredoc_len = 0;
  267. }
  268. break;
  269. default:
  270. valid_end = 0;
  271. break;
  272. }
  273. break;
  274. case sstring:
  275. if (code[i] == '\\') {
  276. code_type = sstring_esc;
  277. } else {
  278. if (code[i] == '\'') {
  279. code_type = body;
  280. }
  281. }
  282. break;
  283. case sstring_esc:
  284. code_type = sstring;
  285. break;
  286. case dstring:
  287. if (code[i] == '\\') {
  288. code_type = dstring_esc;
  289. } else {
  290. if (code[i] == '"') {
  291. code_type = body;
  292. }
  293. }
  294. break;
  295. case dstring_esc:
  296. code_type = dstring;
  297. break;
  298. case comment_line:
  299. if (code[i] == '\n') {
  300. code_type = body;
  301. }
  302. break;
  303. case comment_block:
  304. if (code[i-1] == '*' && code[i] == '/') {
  305. code_type = body;
  306. valid_end = last_valid_end;
  307. }
  308. break;
  309. case heredoc_start:
  310. switch(code[i]) {
  311. case ' ':
  312. case '\t':
  313. case '\'':
  314. break;
  315. case '\r':
  316. case '\n':
  317. if (heredoc_tag) {
  318. code_type = heredoc;
  319. } else {
  320. /* Malformed heredoc without label */
  321. code_type = body;
  322. }
  323. break;
  324. default:
  325. if (!heredoc_tag) {
  326. heredoc_tag = code+i;
  327. }
  328. heredoc_len++;
  329. break;
  330. }
  331. break;
  332. case heredoc:
  333. ZEND_ASSERT(heredoc_tag);
  334. if (!strncmp(code + i - heredoc_len + 1, heredoc_tag, heredoc_len)) {
  335. unsigned char c = code[i + 1];
  336. char *p = code + i - heredoc_len;
  337. if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c >= 0x80) break;
  338. while (*p == ' ' || *p == '\t') p--;
  339. if (*p != '\n') break;
  340. code_type = body;
  341. }
  342. break;
  343. case outside:
  344. if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
  345. || (i > 3 && !strncmp(code+i-4, "<?php", 5))
  346. ) {
  347. code_type = body;
  348. }
  349. break;
  350. }
  351. }
  352. switch (code_type) {
  353. default:
  354. if (brace_count) {
  355. *prompt = cli_get_prompt("php", '(');
  356. } else if (brackets_count) {
  357. *prompt = cli_get_prompt("php", '{');
  358. } else {
  359. *prompt = cli_get_prompt("php", '>');
  360. }
  361. break;
  362. case sstring:
  363. case sstring_esc:
  364. *prompt = cli_get_prompt("php", '\'');
  365. break;
  366. case dstring:
  367. case dstring_esc:
  368. *prompt = cli_get_prompt("php", '"');
  369. break;
  370. case comment_block:
  371. *prompt = cli_get_prompt("/* ", '>');
  372. break;
  373. case heredoc:
  374. *prompt = cli_get_prompt("<<<", '>');
  375. break;
  376. case outside:
  377. *prompt = cli_get_prompt(" ", '>');
  378. break;
  379. }
  380. if (!valid_end || brackets_count) {
  381. return 0;
  382. } else {
  383. return 1;
  384. }
  385. }
  386. /* }}} */
  387. static char *cli_completion_generator_ht(const char *text, size_t textlen, int *state, HashTable *ht, void **pData) /* {{{ */
  388. {
  389. zend_string *name;
  390. zend_ulong number;
  391. if (!(*state % 2)) {
  392. zend_hash_internal_pointer_reset(ht);
  393. (*state)++;
  394. }
  395. while(zend_hash_has_more_elements(ht) == SUCCESS) {
  396. zend_hash_get_current_key(ht, &name, &number);
  397. if (!textlen || !strncmp(ZSTR_VAL(name), text, textlen)) {
  398. if (pData) {
  399. *pData = zend_hash_get_current_data_ptr(ht);
  400. }
  401. zend_hash_move_forward(ht);
  402. return ZSTR_VAL(name);
  403. }
  404. if (zend_hash_move_forward(ht) == FAILURE) {
  405. break;
  406. }
  407. }
  408. (*state)++;
  409. return NULL;
  410. } /* }}} */
  411. static char *cli_completion_generator_var(const char *text, size_t textlen, int *state) /* {{{ */
  412. {
  413. char *retval, *tmp;
  414. zend_array *symbol_table = &EG(symbol_table);
  415. tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, symbol_table, NULL);
  416. if (retval) {
  417. retval = malloc(strlen(tmp) + 2);
  418. retval[0] = '$';
  419. strcpy(&retval[1], tmp);
  420. rl_completion_append_character = '\0';
  421. }
  422. return retval;
  423. } /* }}} */
  424. static char *cli_completion_generator_ini(const char *text, size_t textlen, int *state) /* {{{ */
  425. {
  426. char *retval, *tmp;
  427. tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(ini_directives), NULL);
  428. if (retval) {
  429. retval = malloc(strlen(tmp) + 2);
  430. retval[0] = '#';
  431. strcpy(&retval[1], tmp);
  432. rl_completion_append_character = '=';
  433. }
  434. return retval;
  435. } /* }}} */
  436. static char *cli_completion_generator_func(const char *text, size_t textlen, int *state, HashTable *ht) /* {{{ */
  437. {
  438. zend_function *func;
  439. char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func);
  440. if (retval) {
  441. rl_completion_append_character = '(';
  442. retval = strdup(ZSTR_VAL(func->common.function_name));
  443. }
  444. return retval;
  445. } /* }}} */
  446. static char *cli_completion_generator_class(const char *text, size_t textlen, int *state) /* {{{ */
  447. {
  448. zend_class_entry *ce;
  449. char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&ce);
  450. if (retval) {
  451. rl_completion_append_character = '\0';
  452. retval = strdup(ZSTR_VAL(ce->name));
  453. }
  454. return retval;
  455. } /* }}} */
  456. static char *cli_completion_generator_define(const char *text, size_t textlen, int *state, HashTable *ht) /* {{{ */
  457. {
  458. zend_class_entry **pce;
  459. char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce);
  460. if (retval) {
  461. rl_completion_append_character = '\0';
  462. retval = strdup(retval);
  463. }
  464. return retval;
  465. } /* }}} */
  466. static int cli_completion_state;
  467. static char *cli_completion_generator(const char *text, int index) /* {{{ */
  468. {
  469. /*
  470. TODO:
  471. - constants
  472. - maybe array keys
  473. - language constructs and other things outside a hashtable (echo, try, function, class, ...)
  474. - object/class members
  475. - future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
  476. */
  477. char *retval = NULL;
  478. size_t textlen = strlen(text);
  479. if (!index) {
  480. cli_completion_state = 0;
  481. }
  482. if (text[0] == '$') {
  483. retval = cli_completion_generator_var(text, textlen, &cli_completion_state);
  484. } else if (text[0] == '#' && text[1] != '[') {
  485. retval = cli_completion_generator_ini(text, textlen, &cli_completion_state);
  486. } else {
  487. char *lc_text, *class_name_end;
  488. zend_string *class_name = NULL;
  489. zend_class_entry *ce = NULL;
  490. class_name_end = strstr(text, "::");
  491. if (class_name_end) {
  492. size_t class_name_len = class_name_end - text;
  493. class_name = zend_string_alloc(class_name_len, 0);
  494. zend_str_tolower_copy(ZSTR_VAL(class_name), text, class_name_len);
  495. if ((ce = zend_lookup_class(class_name)) == NULL) {
  496. zend_string_release_ex(class_name, 0);
  497. return NULL;
  498. }
  499. lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
  500. textlen -= (class_name_len + 2);
  501. } else {
  502. lc_text = zend_str_tolower_dup(text, textlen);
  503. }
  504. switch (cli_completion_state) {
  505. case 0:
  506. case 1:
  507. retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, ce ? &ce->function_table : EG(function_table));
  508. if (retval) {
  509. break;
  510. }
  511. ZEND_FALLTHROUGH;
  512. case 2:
  513. case 3:
  514. retval = cli_completion_generator_define(text, textlen, &cli_completion_state, ce ? &ce->constants_table : EG(zend_constants));
  515. if (retval || ce) {
  516. break;
  517. }
  518. ZEND_FALLTHROUGH;
  519. case 4:
  520. case 5:
  521. retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state);
  522. break;
  523. default:
  524. break;
  525. }
  526. efree(lc_text);
  527. if (class_name) {
  528. zend_string_release_ex(class_name, 0);
  529. }
  530. if (ce && retval) {
  531. size_t len = ZSTR_LEN(ce->name) + 2 + strlen(retval) + 1;
  532. char *tmp = malloc(len);
  533. snprintf(tmp, len, "%s::%s", ZSTR_VAL(ce->name), retval);
  534. free(retval);
  535. retval = tmp;
  536. }
  537. }
  538. return retval;
  539. } /* }}} */
  540. static char **cli_code_completion(const char *text, int start, int end) /* {{{ */
  541. {
  542. return rl_completion_matches(text, cli_completion_generator);
  543. }
  544. /* }}} */
  545. static int readline_shell_run(void) /* {{{ */
  546. {
  547. char *line;
  548. size_t size = 4096, pos = 0, len;
  549. char *code = emalloc(size);
  550. zend_string *prompt = cli_get_prompt("php", '>');
  551. char *history_file;
  552. int history_lines_to_write = 0;
  553. if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) {
  554. zend_file_handle prepend_file;
  555. zend_stream_init_filename(&prepend_file, PG(auto_prepend_file));
  556. zend_execute_scripts(ZEND_REQUIRE, NULL, 1, &prepend_file);
  557. zend_destroy_file_handle(&prepend_file);
  558. }
  559. #ifndef PHP_WIN32
  560. history_file = tilde_expand("~/.php_history");
  561. #else
  562. spprintf(&history_file, MAX_PATH, "%s/.php_history", getenv("USERPROFILE"));
  563. #endif
  564. /* Install the default completion function for 'php -a'.
  565. *
  566. * But if readline_completion_function() was called by PHP code prior to the shell starting
  567. * (e.g. with 'php -d auto_prepend_file=prepend.php -a'),
  568. * then use that instead of PHP's default. */
  569. if (rl_attempted_completion_function != php_readline_completion_cb) {
  570. rl_attempted_completion_function = cli_code_completion;
  571. }
  572. #ifndef PHP_WIN32
  573. rl_special_prefixes = "$";
  574. #endif
  575. read_history(history_file);
  576. EG(exit_status) = 0;
  577. while ((line = readline(ZSTR_VAL(prompt))) != NULL) {
  578. if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) {
  579. free(line);
  580. break;
  581. }
  582. if (!pos && !*line) {
  583. free(line);
  584. continue;
  585. }
  586. len = strlen(line);
  587. if (line[0] == '#' && line[1] != '[') {
  588. char *param = strstr(&line[1], "=");
  589. if (param) {
  590. zend_string *cmd;
  591. param++;
  592. cmd = zend_string_init(&line[1], param - &line[1] - 1, 0);
  593. zend_alter_ini_entry_chars_ex(cmd, param, strlen(param), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
  594. zend_string_release_ex(cmd, 0);
  595. add_history(line);
  596. zend_string_release_ex(prompt, 0);
  597. /* TODO: This might be wrong! */
  598. prompt = cli_get_prompt("php", '>');
  599. continue;
  600. }
  601. }
  602. if (pos + len + 2 > size) {
  603. size = pos + len + 2;
  604. code = erealloc(code, size);
  605. }
  606. memcpy(&code[pos], line, len);
  607. pos += len;
  608. code[pos] = '\n';
  609. code[++pos] = '\0';
  610. if (*line) {
  611. add_history(line);
  612. history_lines_to_write += 1;
  613. }
  614. free(line);
  615. zend_string_release_ex(prompt, 0);
  616. if (!cli_is_valid_code(code, pos, &prompt)) {
  617. continue;
  618. }
  619. if (history_lines_to_write) {
  620. #if HAVE_LIBEDIT
  621. write_history(history_file);
  622. #else
  623. append_history(history_lines_to_write, history_file);
  624. #endif
  625. history_lines_to_write = 0;
  626. }
  627. zend_try {
  628. zend_eval_stringl(code, pos, NULL, "php shell code");
  629. } zend_end_try();
  630. pos = 0;
  631. if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') {
  632. php_write("\n", 1);
  633. }
  634. if (EG(exception)) {
  635. zend_exception_error(EG(exception), E_WARNING);
  636. }
  637. if (pager_pipe) {
  638. fclose(pager_pipe);
  639. pager_pipe = NULL;
  640. }
  641. php_last_char = '\0';
  642. }
  643. #ifdef PHP_WIN32
  644. efree(history_file);
  645. #else
  646. free(history_file);
  647. #endif
  648. efree(code);
  649. zend_string_release_ex(prompt, 0);
  650. return EG(exit_status);
  651. }
  652. /* }}} */
  653. #ifdef PHP_WIN32
  654. typedef cli_shell_callbacks_t *(__cdecl *get_cli_shell_callbacks)(void);
  655. #define GET_SHELL_CB(cb) \
  656. do { \
  657. get_cli_shell_callbacks get_callbacks; \
  658. HMODULE hMod = GetModuleHandle("php.exe"); \
  659. (cb) = NULL; \
  660. if (strlen(sapi_module.name) >= 3 && 0 == strncmp("cli", sapi_module.name, 3)) { \
  661. get_callbacks = (get_cli_shell_callbacks)GetProcAddress(hMod, "php_cli_get_shell_callbacks"); \
  662. if (get_callbacks) { \
  663. (cb) = get_callbacks(); \
  664. } \
  665. } \
  666. } while(0)
  667. #else
  668. /*
  669. #ifdef COMPILE_DL_READLINE
  670. This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only
  671. extensions. If that is being changed dlsym() should only be used when building
  672. this extension sharedto offer compatibility.
  673. */
  674. #define GET_SHELL_CB(cb) \
  675. do { \
  676. (cb) = NULL; \
  677. cli_shell_callbacks_t *(*get_callbacks)(void); \
  678. get_callbacks = dlsym(RTLD_DEFAULT, "php_cli_get_shell_callbacks"); \
  679. if (get_callbacks) { \
  680. (cb) = get_callbacks(); \
  681. } \
  682. } while(0)
  683. /*#else
  684. #define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks()
  685. #endif*/
  686. #endif
  687. PHP_MINIT_FUNCTION(cli_readline)
  688. {
  689. cli_shell_callbacks_t *cb;
  690. ZEND_INIT_MODULE_GLOBALS(cli_readline, cli_readline_init_globals, NULL);
  691. REGISTER_INI_ENTRIES();
  692. #if HAVE_LIBEDIT
  693. REGISTER_STRING_CONSTANT("READLINE_LIB", "libedit", CONST_CS|CONST_PERSISTENT);
  694. #else
  695. REGISTER_STRING_CONSTANT("READLINE_LIB", "readline", CONST_CS|CONST_PERSISTENT);
  696. #endif
  697. GET_SHELL_CB(cb);
  698. if (cb) {
  699. cb->cli_shell_write = readline_shell_write;
  700. cb->cli_shell_ub_write = readline_shell_ub_write;
  701. cb->cli_shell_run = readline_shell_run;
  702. }
  703. return SUCCESS;
  704. }
  705. PHP_MSHUTDOWN_FUNCTION(cli_readline)
  706. {
  707. cli_shell_callbacks_t *cb;
  708. UNREGISTER_INI_ENTRIES();
  709. GET_SHELL_CB(cb);
  710. if (cb) {
  711. cb->cli_shell_write = NULL;
  712. cb->cli_shell_ub_write = NULL;
  713. cb->cli_shell_run = NULL;
  714. }
  715. return SUCCESS;
  716. }
  717. PHP_MINFO_FUNCTION(cli_readline)
  718. {
  719. php_info_print_table_start();
  720. php_info_print_table_header(2, "Readline Support", "enabled");
  721. #ifdef PHP_WIN32
  722. php_info_print_table_row(2, "Readline library", "WinEditLine");
  723. #else
  724. php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown"));
  725. #endif
  726. php_info_print_table_end();
  727. DISPLAY_INI_ENTRIES();
  728. }