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