excallback.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. From: Jeff Solomon <jsolomon@stanford.edu>
  3. Date: Fri, 9 Apr 1999 10:13:27 -0700 (PDT)
  4. To: chet@po.cwru.edu
  5. Subject: new readline example
  6. Message-ID: <14094.12094.527305.199695@mrclean.Stanford.EDU>
  7. Chet,
  8. I've been using readline 4.0. Specifically, I've been using the perl
  9. version Term::ReadLine::Gnu. It works great.
  10. Anyway, I've been playing around the alternate interface and I wanted
  11. to contribute a little C program, callback.c, to you that you could
  12. use as an example of the alternate interface in the /examples
  13. directory of the readline distribution.
  14. My example shows how, using the alternate interface, you can
  15. interactively change the prompt (which is very nice imo). Also, I
  16. point out that you must roll your own terminal setting when using the
  17. alternate interface because readline depreps (using your parlance) the
  18. terminal while in the user callback. I try to demostrate what I mean
  19. with an example. I've included the program below.
  20. To compile, I just put the program in the examples directory and made
  21. the appropriate changes to the EXECUTABLES and OBJECTS line and added
  22. an additional target 'callback'.
  23. I compiled on my Sun Solaris2.6 box using Sun's cc.
  24. Let me know what you think.
  25. Jeff
  26. */
  27. /*
  28. Copyright (C) 1999 Jeff Solomon
  29. */
  30. #if defined (HAVE_CONFIG_H)
  31. #include <config.h>
  32. #endif
  33. #include <stdio.h>
  34. #include <sys/types.h>
  35. #ifdef HAVE_UNISTD_H
  36. #include <unistd.h>
  37. #endif
  38. #include <termios.h> /* xxx - should make this more general */
  39. #ifdef READLINE_LIBRARY
  40. # include "readline.h"
  41. #else
  42. # include <readline/readline.h>
  43. #endif
  44. /* This little examples demonstrates the alternate interface to using readline.
  45. * In the alternate interface, the user maintains control over program flow and
  46. * only calls readline when STDIN is readable. Using the alternate interface,
  47. * you can do anything else while still using readline (like talking to a
  48. * network or another program) without blocking.
  49. *
  50. * Specifically, this program highlights two importants features of the
  51. * alternate interface. The first is the ability to interactively change the
  52. * prompt, which can't be done using the regular interface since rl_prompt is
  53. * read-only.
  54. *
  55. * The second feature really highlights a subtle point when using the alternate
  56. * interface. That is, readline will not alter the terminal when inside your
  57. * callback handler. So let's so, your callback executes a user command that
  58. * takes a non-trivial amount of time to complete (seconds). While your
  59. * executing the command, the user continues to type keystrokes and expects them
  60. * to be re-echoed on the new prompt when it returns. Unfortunately, the default
  61. * terminal configuration doesn't do this. After the prompt returns, the user
  62. * must hit one additional keystroke and then will see all of his previous
  63. * keystrokes. To illustrate this, compile and run this program. Type "sleep" at
  64. * the prompt and then type "bar" before the prompt returns (you have 3
  65. * seconds). Notice how "bar" is re-echoed on the prompt after the prompt
  66. * returns? This is what you expect to happen. Now comment out the 4 lines below
  67. * the line that says COMMENT LINE BELOW. Recompile and rerun the program and do
  68. * the same thing. When the prompt returns, you should not see "bar". Now type
  69. * "f", see how "barf" magically appears? This behavior is un-expected and not
  70. * desired.
  71. */
  72. void process_line(char *line);
  73. int change_prompt(void);
  74. char *get_prompt(void);
  75. int prompt = 1;
  76. char prompt_buf[40], line_buf[256];
  77. tcflag_t old_lflag;
  78. cc_t old_vtime;
  79. struct termios term;
  80. int
  81. main()
  82. {
  83. fd_set fds;
  84. /* Adjust the terminal slightly before the handler is installed. Disable
  85. * canonical mode processing and set the input character time flag to be
  86. * non-blocking.
  87. */
  88. if( tcgetattr(STDIN_FILENO, &term) < 0 ) {
  89. perror("tcgetattr");
  90. exit(1);
  91. }
  92. old_lflag = term.c_lflag;
  93. old_vtime = term.c_cc[VTIME];
  94. term.c_lflag &= ~ICANON;
  95. term.c_cc[VTIME] = 1;
  96. /* COMMENT LINE BELOW - see above */
  97. if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) {
  98. perror("tcsetattr");
  99. exit(1);
  100. }
  101. rl_add_defun("change-prompt", change_prompt, CTRL('t'));
  102. rl_callback_handler_install(get_prompt(), process_line);
  103. while(1) {
  104. FD_ZERO(&fds);
  105. FD_SET(fileno(stdin), &fds);
  106. if( select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
  107. perror("select");
  108. exit(1);
  109. }
  110. if( FD_ISSET(fileno(stdin), &fds) ) {
  111. rl_callback_read_char();
  112. }
  113. }
  114. }
  115. void
  116. process_line(char *line)
  117. {
  118. if( line == NULL ) {
  119. fprintf(stderr, "\n", line);
  120. /* reset the old terminal setting before exiting */
  121. term.c_lflag = old_lflag;
  122. term.c_cc[VTIME] = old_vtime;
  123. if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) {
  124. perror("tcsetattr");
  125. exit(1);
  126. }
  127. exit(0);
  128. }
  129. if( strcmp(line, "sleep") == 0 ) {
  130. sleep(3);
  131. } else {
  132. fprintf(stderr, "|%s|\n", line);
  133. }
  134. free (line);
  135. }
  136. int
  137. change_prompt(void)
  138. {
  139. /* toggle the prompt variable */
  140. prompt = !prompt;
  141. /* save away the current contents of the line */
  142. strcpy(line_buf, rl_line_buffer);
  143. /* install a new handler which will change the prompt and erase the current line */
  144. rl_callback_handler_install(get_prompt(), process_line);
  145. /* insert the old text on the new line */
  146. rl_insert_text(line_buf);
  147. /* redraw the current line - this is an undocumented function. It invokes the
  148. * redraw-current-line command.
  149. */
  150. rl_refresh_line(0, 0);
  151. }
  152. char *
  153. get_prompt(void)
  154. {
  155. /* The prompts can even be different lengths! */
  156. sprintf(prompt_buf, "%s",
  157. prompt ? "Hit ctrl-t to toggle prompt> " : "Pretty cool huh?> ");
  158. return prompt_buf;
  159. }