You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

645 lines
16 KiB

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