readline_cli.c 18 KB

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