readline_cli.c 19 KB

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