readline.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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: Thies C. Arntzen <thies@thieso.net> |
  14. +----------------------------------------------------------------------+
  15. */
  16. /* {{{ includes & prototypes */
  17. #ifdef HAVE_CONFIG_H
  18. #include "config.h"
  19. #endif
  20. #include "php.h"
  21. #include "php_readline.h"
  22. #include "readline_cli.h"
  23. #include "readline_arginfo.h"
  24. #if HAVE_LIBREADLINE || HAVE_LIBEDIT
  25. #ifndef HAVE_RL_COMPLETION_MATCHES
  26. #define rl_completion_matches completion_matches
  27. #endif
  28. #ifdef HAVE_LIBEDIT
  29. #include <editline/readline.h>
  30. #else
  31. #include <readline/readline.h>
  32. #include <readline/history.h>
  33. #endif
  34. #if HAVE_RL_CALLBACK_READ_CHAR
  35. static zval _prepped_callback;
  36. #endif
  37. static zval _readline_completion;
  38. static zval _readline_array;
  39. PHP_MINIT_FUNCTION(readline);
  40. PHP_MSHUTDOWN_FUNCTION(readline);
  41. PHP_RSHUTDOWN_FUNCTION(readline);
  42. PHP_MINFO_FUNCTION(readline);
  43. /* }}} */
  44. /* {{{ module stuff */
  45. zend_module_entry readline_module_entry = {
  46. STANDARD_MODULE_HEADER,
  47. "readline",
  48. ext_functions,
  49. PHP_MINIT(readline),
  50. PHP_MSHUTDOWN(readline),
  51. NULL,
  52. PHP_RSHUTDOWN(readline),
  53. PHP_MINFO(readline),
  54. PHP_READLINE_VERSION,
  55. STANDARD_MODULE_PROPERTIES
  56. };
  57. #ifdef COMPILE_DL_READLINE
  58. ZEND_GET_MODULE(readline)
  59. #endif
  60. PHP_MINIT_FUNCTION(readline)
  61. {
  62. #if HAVE_LIBREADLINE
  63. /* libedit don't need this call which set the tty in cooked mode */
  64. using_history();
  65. #endif
  66. ZVAL_UNDEF(&_readline_completion);
  67. #if HAVE_RL_CALLBACK_READ_CHAR
  68. ZVAL_UNDEF(&_prepped_callback);
  69. #endif
  70. return PHP_MINIT(cli_readline)(INIT_FUNC_ARGS_PASSTHRU);
  71. }
  72. PHP_MSHUTDOWN_FUNCTION(readline)
  73. {
  74. return PHP_MSHUTDOWN(cli_readline)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
  75. }
  76. PHP_RSHUTDOWN_FUNCTION(readline)
  77. {
  78. zval_ptr_dtor(&_readline_completion);
  79. ZVAL_UNDEF(&_readline_completion);
  80. #if HAVE_RL_CALLBACK_READ_CHAR
  81. if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
  82. rl_callback_handler_remove();
  83. zval_ptr_dtor(&_prepped_callback);
  84. ZVAL_UNDEF(&_prepped_callback);
  85. }
  86. #endif
  87. return SUCCESS;
  88. }
  89. PHP_MINFO_FUNCTION(readline)
  90. {
  91. PHP_MINFO(cli_readline)(ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU);
  92. }
  93. /* }}} */
  94. /* {{{ Reads a line */
  95. PHP_FUNCTION(readline)
  96. {
  97. char *prompt = NULL;
  98. size_t prompt_len;
  99. char *result;
  100. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &prompt, &prompt_len)) {
  101. RETURN_THROWS();
  102. }
  103. result = readline(prompt);
  104. if (! result) {
  105. RETURN_FALSE;
  106. } else {
  107. RETVAL_STRING(result);
  108. free(result);
  109. }
  110. }
  111. /* }}} */
  112. #define SAFE_STRING(s) ((s)?(char*)(s):"")
  113. /* {{{ Gets/sets various internal readline variables. */
  114. PHP_FUNCTION(readline_info)
  115. {
  116. zend_string *what = NULL;
  117. zval *value = NULL;
  118. size_t oldval;
  119. char *oldstr;
  120. if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!z!", &what, &value) == FAILURE) {
  121. RETURN_THROWS();
  122. }
  123. if (!what) {
  124. array_init(return_value);
  125. add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer));
  126. add_assoc_long(return_value,"point",rl_point);
  127. #ifndef PHP_WIN32
  128. add_assoc_long(return_value,"end",rl_end);
  129. #endif
  130. #ifdef HAVE_LIBREADLINE
  131. add_assoc_long(return_value,"mark",rl_mark);
  132. add_assoc_long(return_value,"done",rl_done);
  133. add_assoc_long(return_value,"pending_input",rl_pending_input);
  134. add_assoc_string(return_value,"prompt",SAFE_STRING(rl_prompt));
  135. add_assoc_string(return_value,"terminal_name",(char *)SAFE_STRING(rl_terminal_name));
  136. add_assoc_str(return_value, "completion_append_character",
  137. rl_completion_append_character == 0
  138. ? ZSTR_EMPTY_ALLOC()
  139. : ZSTR_CHAR(rl_completion_append_character));
  140. add_assoc_bool(return_value,"completion_suppress_append",rl_completion_suppress_append);
  141. #endif
  142. #if HAVE_ERASE_EMPTY_LINE
  143. add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line);
  144. #endif
  145. #ifndef PHP_WIN32
  146. add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version));
  147. #endif
  148. add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name));
  149. add_assoc_long(return_value,"attempted_completion_over",rl_attempted_completion_over);
  150. } else {
  151. if (zend_string_equals_literal_ci(what,"line_buffer")) {
  152. oldstr = rl_line_buffer;
  153. if (value) {
  154. /* XXX if (rl_line_buffer) free(rl_line_buffer); */
  155. if (!try_convert_to_string(value)) {
  156. RETURN_THROWS();
  157. }
  158. rl_line_buffer = strdup(Z_STRVAL_P(value));
  159. }
  160. RETVAL_STRING(SAFE_STRING(oldstr));
  161. } else if (zend_string_equals_literal_ci(what, "point")) {
  162. RETVAL_LONG(rl_point);
  163. #ifndef PHP_WIN32
  164. } else if (zend_string_equals_literal_ci(what, "end")) {
  165. RETVAL_LONG(rl_end);
  166. #endif
  167. #ifdef HAVE_LIBREADLINE
  168. } else if (zend_string_equals_literal_ci(what, "mark")) {
  169. RETVAL_LONG(rl_mark);
  170. } else if (zend_string_equals_literal_ci(what, "done")) {
  171. oldval = rl_done;
  172. if (value) {
  173. rl_done = zval_get_long(value);
  174. }
  175. RETVAL_LONG(oldval);
  176. } else if (zend_string_equals_literal_ci(what, "pending_input")) {
  177. oldval = rl_pending_input;
  178. if (value) {
  179. if (!try_convert_to_string(value)) {
  180. RETURN_THROWS();
  181. }
  182. rl_pending_input = Z_STRVAL_P(value)[0];
  183. }
  184. RETVAL_LONG(oldval);
  185. } else if (zend_string_equals_literal_ci(what, "prompt")) {
  186. RETVAL_STRING(SAFE_STRING(rl_prompt));
  187. } else if (zend_string_equals_literal_ci(what, "terminal_name")) {
  188. RETVAL_STRING((char *)SAFE_STRING(rl_terminal_name));
  189. } else if (zend_string_equals_literal_ci(what, "completion_suppress_append")) {
  190. oldval = rl_completion_suppress_append;
  191. if (value) {
  192. rl_completion_suppress_append = zend_is_true(value);
  193. }
  194. RETVAL_BOOL(oldval);
  195. } else if (zend_string_equals_literal_ci(what, "completion_append_character")) {
  196. oldval = rl_completion_append_character;
  197. if (value) {
  198. if (!try_convert_to_string(value)) {
  199. RETURN_THROWS();
  200. }
  201. rl_completion_append_character = (int)Z_STRVAL_P(value)[0];
  202. }
  203. RETVAL_INTERNED_STR(
  204. oldval == 0 ? ZSTR_EMPTY_ALLOC() : ZSTR_CHAR(oldval));
  205. #endif
  206. #if HAVE_ERASE_EMPTY_LINE
  207. } else if (zend_string_equals_literal_ci(what, "erase_empty_line")) {
  208. oldval = rl_erase_empty_line;
  209. if (value) {
  210. rl_erase_empty_line = zval_get_long(value);
  211. }
  212. RETVAL_LONG(oldval);
  213. #endif
  214. #ifndef PHP_WIN32
  215. } else if (zend_string_equals_literal_ci(what,"library_version")) {
  216. RETVAL_STRING((char *)SAFE_STRING(rl_library_version));
  217. #endif
  218. } else if (zend_string_equals_literal_ci(what, "readline_name")) {
  219. oldstr = (char*)rl_readline_name;
  220. if (value) {
  221. /* XXX if (rl_readline_name) free(rl_readline_name); */
  222. if (!try_convert_to_string(value)) {
  223. RETURN_THROWS();
  224. }
  225. rl_readline_name = strdup(Z_STRVAL_P(value));
  226. }
  227. RETVAL_STRING(SAFE_STRING(oldstr));
  228. } else if (zend_string_equals_literal_ci(what, "attempted_completion_over")) {
  229. oldval = rl_attempted_completion_over;
  230. if (value) {
  231. rl_attempted_completion_over = zval_get_long(value);
  232. }
  233. RETVAL_LONG(oldval);
  234. }
  235. }
  236. }
  237. /* }}} */
  238. /* {{{ Adds a line to the history */
  239. PHP_FUNCTION(readline_add_history)
  240. {
  241. char *arg;
  242. size_t arg_len;
  243. if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
  244. RETURN_THROWS();
  245. }
  246. add_history(arg);
  247. RETURN_TRUE;
  248. }
  249. /* }}} */
  250. /* {{{ Clears the history */
  251. PHP_FUNCTION(readline_clear_history)
  252. {
  253. if (zend_parse_parameters_none() == FAILURE) {
  254. RETURN_THROWS();
  255. }
  256. #if HAVE_LIBEDIT
  257. /* clear_history is the only function where rl_initialize
  258. is not call to ensure correct allocation */
  259. using_history();
  260. #endif
  261. clear_history();
  262. RETURN_TRUE;
  263. }
  264. /* }}} */
  265. #ifdef HAVE_HISTORY_LIST
  266. /* {{{ Lists the history */
  267. PHP_FUNCTION(readline_list_history)
  268. {
  269. HIST_ENTRY **history;
  270. if (zend_parse_parameters_none() == FAILURE) {
  271. RETURN_THROWS();
  272. }
  273. array_init(return_value);
  274. #if defined(HAVE_LIBEDIT) && defined(PHP_WIN32) /* Winedit on Windows */
  275. history = history_list();
  276. if (history) {
  277. int i, n = history_length();
  278. for (i = 0; i < n; i++) {
  279. add_next_index_string(return_value, history[i]->line);
  280. }
  281. }
  282. #elif defined(HAVE_LIBEDIT) /* libedit */
  283. {
  284. HISTORY_STATE *hs;
  285. int i;
  286. using_history();
  287. hs = history_get_history_state();
  288. if (hs && hs->length) {
  289. history = history_list();
  290. if (history) {
  291. for (i = 0; i < hs->length; i++) {
  292. add_next_index_string(return_value, history[i]->line);
  293. }
  294. }
  295. }
  296. free(hs);
  297. }
  298. #else /* readline */
  299. history = history_list();
  300. if (history) {
  301. int i;
  302. for (i = 0; history[i]; i++) {
  303. add_next_index_string(return_value, history[i]->line);
  304. }
  305. }
  306. #endif
  307. }
  308. /* }}} */
  309. #endif
  310. /* {{{ Reads the history */
  311. PHP_FUNCTION(readline_read_history)
  312. {
  313. char *arg = NULL;
  314. size_t arg_len;
  315. if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!", &arg, &arg_len) == FAILURE) {
  316. RETURN_THROWS();
  317. }
  318. if (arg && php_check_open_basedir(arg)) {
  319. RETURN_FALSE;
  320. }
  321. /* XXX from & to NYI */
  322. if (read_history(arg)) {
  323. /* If filename is NULL, then read from `~/.history' */
  324. RETURN_FALSE;
  325. } else {
  326. RETURN_TRUE;
  327. }
  328. }
  329. /* }}} */
  330. /* {{{ Writes the history */
  331. PHP_FUNCTION(readline_write_history)
  332. {
  333. char *arg = NULL;
  334. size_t arg_len;
  335. if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!", &arg, &arg_len) == FAILURE) {
  336. RETURN_THROWS();
  337. }
  338. if (arg && php_check_open_basedir(arg)) {
  339. RETURN_FALSE;
  340. }
  341. if (write_history(arg)) {
  342. RETURN_FALSE;
  343. } else {
  344. RETURN_TRUE;
  345. }
  346. }
  347. /* }}} */
  348. /* {{{ Readline completion function? */
  349. static char *_readline_command_generator(const char *text, int state)
  350. {
  351. HashTable *myht = Z_ARRVAL(_readline_array);
  352. zval *entry;
  353. if (!state) {
  354. zend_hash_internal_pointer_reset(myht);
  355. }
  356. while ((entry = zend_hash_get_current_data(myht)) != NULL) {
  357. zend_hash_move_forward(myht);
  358. convert_to_string(entry);
  359. if (strncmp (Z_STRVAL_P(entry), text, strlen(text)) == 0) {
  360. return (strdup(Z_STRVAL_P(entry)));
  361. }
  362. }
  363. return NULL;
  364. }
  365. static void _readline_string_zval(zval *ret, const char *str)
  366. {
  367. if (str) {
  368. ZVAL_STRING(ret, (char*)str);
  369. } else {
  370. ZVAL_NULL(ret);
  371. }
  372. }
  373. static void _readline_long_zval(zval *ret, long l)
  374. {
  375. ZVAL_LONG(ret, l);
  376. }
  377. char **php_readline_completion_cb(const char *text, int start, int end)
  378. {
  379. zval params[3];
  380. char **matches = NULL;
  381. _readline_string_zval(&params[0], text);
  382. _readline_long_zval(&params[1], start);
  383. _readline_long_zval(&params[2], end);
  384. if (call_user_function(NULL, NULL, &_readline_completion, &_readline_array, 3, params) == SUCCESS) {
  385. if (Z_TYPE(_readline_array) == IS_ARRAY) {
  386. SEPARATE_ARRAY(&_readline_array);
  387. if (zend_hash_num_elements(Z_ARRVAL(_readline_array))) {
  388. matches = rl_completion_matches(text,_readline_command_generator);
  389. } else {
  390. /* libedit will read matches[2] */
  391. matches = calloc(sizeof(char *), 3);
  392. if (!matches) {
  393. return NULL;
  394. }
  395. matches[0] = strdup("");
  396. }
  397. }
  398. }
  399. zval_ptr_dtor(&params[0]);
  400. zval_ptr_dtor(&_readline_array);
  401. return matches;
  402. }
  403. PHP_FUNCTION(readline_completion_function)
  404. {
  405. zend_fcall_info fci;
  406. zend_fcall_info_cache fcc;
  407. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "f", &fci, &fcc)) {
  408. RETURN_THROWS();
  409. }
  410. zval_ptr_dtor(&_readline_completion);
  411. ZVAL_COPY(&_readline_completion, &fci.function_name);
  412. /* NOTE: The rl_attempted_completion_function variable (and others) are part of the readline library, not php */
  413. rl_attempted_completion_function = php_readline_completion_cb;
  414. if (rl_attempted_completion_function == NULL) {
  415. RETURN_FALSE;
  416. }
  417. RETURN_TRUE;
  418. }
  419. /* }}} */
  420. #if HAVE_RL_CALLBACK_READ_CHAR
  421. static void php_rl_callback_handler(char *the_line)
  422. {
  423. zval params[1];
  424. zval dummy;
  425. ZVAL_NULL(&dummy);
  426. _readline_string_zval(&params[0], the_line);
  427. call_user_function(NULL, NULL, &_prepped_callback, &dummy, 1, params);
  428. zval_ptr_dtor(&params[0]);
  429. zval_ptr_dtor(&dummy);
  430. }
  431. /* {{{ Initializes the readline callback interface and terminal, prints the prompt and returns immediately */
  432. PHP_FUNCTION(readline_callback_handler_install)
  433. {
  434. char *prompt;
  435. zend_fcall_info fci;
  436. zend_fcall_info_cache fcc;
  437. size_t prompt_len;
  438. if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "sf", &prompt, &prompt_len, &fci, &fcc)) {
  439. RETURN_THROWS();
  440. }
  441. if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
  442. rl_callback_handler_remove();
  443. zval_ptr_dtor(&_prepped_callback);
  444. }
  445. ZVAL_COPY(&_prepped_callback, &fci.function_name);
  446. rl_callback_handler_install(prompt, php_rl_callback_handler);
  447. RETURN_TRUE;
  448. }
  449. /* }}} */
  450. /* {{{ Informs the readline callback interface that a character is ready for input */
  451. PHP_FUNCTION(readline_callback_read_char)
  452. {
  453. if (zend_parse_parameters_none() == FAILURE) {
  454. RETURN_THROWS();
  455. }
  456. if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
  457. rl_callback_read_char();
  458. }
  459. }
  460. /* }}} */
  461. /* {{{ Removes a previously installed callback handler and restores terminal settings */
  462. PHP_FUNCTION(readline_callback_handler_remove)
  463. {
  464. if (zend_parse_parameters_none() == FAILURE) {
  465. RETURN_THROWS();
  466. }
  467. if (Z_TYPE(_prepped_callback) != IS_UNDEF) {
  468. rl_callback_handler_remove();
  469. zval_ptr_dtor(&_prepped_callback);
  470. ZVAL_UNDEF(&_prepped_callback);
  471. RETURN_TRUE;
  472. }
  473. RETURN_FALSE;
  474. }
  475. /* }}} */
  476. /* {{{ Ask readline to redraw the display */
  477. PHP_FUNCTION(readline_redisplay)
  478. {
  479. if (zend_parse_parameters_none() == FAILURE) {
  480. RETURN_THROWS();
  481. }
  482. #if HAVE_LIBEDIT
  483. /* seems libedit doesn't take care of rl_initialize in rl_redisplay
  484. * see bug #72538 */
  485. using_history();
  486. #endif
  487. rl_redisplay();
  488. }
  489. /* }}} */
  490. #endif
  491. #if HAVE_RL_ON_NEW_LINE
  492. /* {{{ Inform readline that the cursor has moved to a new line */
  493. PHP_FUNCTION(readline_on_new_line)
  494. {
  495. if (zend_parse_parameters_none() == FAILURE) {
  496. RETURN_THROWS();
  497. }
  498. rl_on_new_line();
  499. }
  500. /* }}} */
  501. #endif
  502. #endif /* HAVE_LIBREADLINE */