2fd06186d29a0ac890a3459e3b999df9616f82d0
[mmh] / sbr / getansreadline.c
1
2 /*
3  * getansreadline.c -- get an answer from the user, with readline
4  *
5  * This code is Copyright (c) 2012, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 #include <h/mh.h>
11 #include <h/signals.h>
12 #include <setjmp.h>
13 #include <signal.h>
14 #include <errno.h>
15
16 #ifdef READLINE_SUPPORT
17 #include <readline/readline.h>
18 #include <readline/history.h>
19
20 static struct swit *rl_cmds;
21
22 static char *nmh_command_generator(const char *, int);
23 static char **nmh_completion(const char *, int, int);
24 static void initialize_readline(void);
25
26 static char ansbuf[BUFSIZ];
27 #if 0
28 static sigjmp_buf sigenv;
29
30 /*
31  * static prototypes
32  */
33 static void intrser (int);
34
35
36 char **
37 getans (char *prompt, struct swit *ansp)
38 {
39     int i;
40     SIGNAL_HANDLER istat = NULL;
41     char *cp, **cpp;
42
43     if (!(sigsetjmp(sigenv, 1))) {
44         istat = SIGNAL (SIGINT, intrser);
45     } else {
46         SIGNAL (SIGINT, istat);
47         return NULL;
48     }
49
50     for (;;) {
51         printf ("%s", prompt);
52         fflush (stdout);
53         cp = ansbuf;
54         while ((i = getchar ()) != '\n') {
55             if (i == EOF) {
56                 /*
57                  * If we get an EOF, return
58                  */
59                 if (feof(stdin))
60                     siglongjmp (sigenv, 1);
61
62                 /*
63                  * For errors, if we get an EINTR that means that we got
64                  * a signal and we should retry.  If we get another error,
65                  * then just return.
66                  */
67
68                 else if (ferror(stdin)) {
69                     if (errno == EINTR) {
70                         clearerr(stdin);
71                         continue;
72                     }
73                     fprintf(stderr, "\nError %s during read\n",
74                             strerror(errno));
75                     siglongjmp (sigenv, 1);
76                 } else {
77                     /*
78                      * Just for completeness's sake ...
79                      */
80
81                     fprintf(stderr, "\nUnknown problem in getchar()\n");
82                     siglongjmp (sigenv, 1);
83                 }
84             }
85             if (cp < &ansbuf[sizeof ansbuf - 1])
86                 *cp++ = i;
87         }
88         *cp = '\0';
89         if (ansbuf[0] == '?' || cp == ansbuf) {
90             printf ("Options are:\n");
91             print_sw (ALL, ansp, "", stdout);
92             continue;
93         }
94         cpp = brkstring (ansbuf, " ", NULL);
95         switch (smatch (*cpp, ansp)) {
96             case AMBIGSW: 
97                 ambigsw (*cpp, ansp);
98                 continue;
99             case UNKWNSW: 
100                 printf (" -%s unknown. Hit <CR> for help.\n", *cpp);
101                 continue;
102             default: 
103                 SIGNAL (SIGINT, istat);
104                 return cpp;
105         }
106     }
107 }
108
109
110 static void
111 intrser (int i)
112 {
113     NMH_UNUSED (i);
114
115     /*
116      * should this be siglongjmp?
117      */
118     siglongjmp (sigenv, 1);
119 }
120 #endif
121
122 /*
123  * getans, but with readline support
124  */
125
126 char **
127 getans_via_readline(char *prompt, struct swit *ansp)
128 {
129     char *ans, **cpp;
130
131     initialize_readline();
132     rl_cmds = ansp;
133
134     for (;;) {
135         ans = readline(prompt);
136         /*
137          * If we get an EOF, return
138          */
139
140         if (ans == NULL)
141             return NULL;
142
143         if (ans[0] == '?' || ans[0] == '\0') {
144             printf("Options are:\n");
145             print_sw(ALL, ansp, "", stdout);
146             free(ans);
147             continue;
148         }
149         add_history(ans);
150         strncpy(ansbuf, ans, sizeof(ansbuf));
151         ansbuf[sizeof(ansbuf) - 1] = '\0';
152         cpp = brkstring(ansbuf, " ", NULL);
153         switch (smatch(*cpp, ansp)) {
154             case AMBIGSW:
155                 ambigsw(*cpp, ansp);
156                 continue;
157             case UNKWNSW:
158                 printf(" -%s unknown. Hit <CR> for help.\n", *cpp);
159                 continue;
160             default:
161                 free(ans);
162                 return cpp;
163         }
164         free(ans);
165     }
166 }
167
168 static void
169 initialize_readline(void)
170 {
171     rl_readline_name = "Nmh";
172     rl_attempted_completion_function = nmh_completion;
173 }
174
175 static char **
176 nmh_completion(const char *text, int start, int end)
177 {
178     char **matches;
179
180     NMH_UNUSED (end);
181
182     matches = (char **) NULL;
183
184     if (start == 0)
185         matches = rl_completion_matches(text, nmh_command_generator);
186
187     return matches;
188 }
189
190 static char *
191 nmh_command_generator(const char *text, int state)
192 {
193     static int list_index, len;
194     char *name, *p;
195     char buf[256];
196
197     if (!state) {
198         list_index = 0;
199         len = strlen(text);
200     }
201
202     while ((name = rl_cmds[list_index].sw)) {
203         list_index++;
204         strncpy(buf, name, sizeof(buf));
205         buf[sizeof(buf) - 1] = '\0';
206         p = *brkstring(buf, " ", NULL);
207         if (strncmp(p, text, len) == 0)
208         return strdup(p);
209     }
210
211     return NULL;
212 }
213 #endif /* READLINE_SUPPORT */
214