ia64/xen-unstable

view tools/ioemu/readline.c @ 7238:971e7c7411b3

Raise an exception if an error appears on the pipes to our children, and make
sure that the child's pipes are closed even under that exception. Move the
handling of POLLHUP to the end of the loop, so that we guarantee to read any
remaining data from the child if POLLHUP and POLLIN appear at the same time.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@ewan
date Thu Oct 06 10:13:11 2005 +0100 (2005-10-06)
parents 8e5fc5fe636c
children f7b43e5c42b9
line source
1 /*
2 * QEMU readline utility
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "vl.h"
26 #define TERM_CMD_BUF_SIZE 4095
27 #define TERM_MAX_CMDS 64
28 #define NB_COMPLETIONS_MAX 256
30 #define IS_NORM 0
31 #define IS_ESC 1
32 #define IS_CSI 2
34 #define printf do_not_use_printf
36 static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
37 static int term_cmd_buf_index;
38 static int term_cmd_buf_size;
40 static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1];
41 static int term_last_cmd_buf_index;
42 static int term_last_cmd_buf_size;
44 static int term_esc_state;
45 static int term_esc_param;
47 static char *term_history[TERM_MAX_CMDS];
48 static int term_hist_entry = -1;
50 static int nb_completions;
51 int completion_index;
52 static char *completions[NB_COMPLETIONS_MAX];
54 static ReadLineFunc *term_readline_func;
55 static int term_is_password;
56 static char term_prompt[256];
57 static void *term_readline_opaque;
59 static void term_show_prompt2(void)
60 {
61 term_printf("%s", term_prompt);
62 term_flush();
63 term_last_cmd_buf_index = 0;
64 term_last_cmd_buf_size = 0;
65 term_esc_state = IS_NORM;
66 }
68 static void term_show_prompt(void)
69 {
70 term_show_prompt2();
71 term_cmd_buf_index = 0;
72 term_cmd_buf_size = 0;
73 }
75 /* update the displayed command line */
76 static void term_update(void)
77 {
78 int i, delta, len;
80 if (term_cmd_buf_size != term_last_cmd_buf_size ||
81 memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
82 for(i = 0; i < term_last_cmd_buf_index; i++) {
83 term_printf("\033[D");
84 }
85 term_cmd_buf[term_cmd_buf_size] = '\0';
86 if (term_is_password) {
87 len = strlen(term_cmd_buf);
88 for(i = 0; i < len; i++)
89 term_printf("*");
90 } else {
91 term_printf("%s", term_cmd_buf);
92 }
93 term_printf("\033[K");
94 memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
95 term_last_cmd_buf_size = term_cmd_buf_size;
96 term_last_cmd_buf_index = term_cmd_buf_size;
97 }
98 if (term_cmd_buf_index != term_last_cmd_buf_index) {
99 delta = term_cmd_buf_index - term_last_cmd_buf_index;
100 if (delta > 0) {
101 for(i = 0;i < delta; i++) {
102 term_printf("\033[C");
103 }
104 } else {
105 delta = -delta;
106 for(i = 0;i < delta; i++) {
107 term_printf("\033[D");
108 }
109 }
110 term_last_cmd_buf_index = term_cmd_buf_index;
111 }
112 term_flush();
113 }
115 static void term_insert_char(int ch)
116 {
117 if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
118 memmove(term_cmd_buf + term_cmd_buf_index + 1,
119 term_cmd_buf + term_cmd_buf_index,
120 term_cmd_buf_size - term_cmd_buf_index);
121 term_cmd_buf[term_cmd_buf_index] = ch;
122 term_cmd_buf_size++;
123 term_cmd_buf_index++;
124 }
125 }
127 static void term_backward_char(void)
128 {
129 if (term_cmd_buf_index > 0) {
130 term_cmd_buf_index--;
131 }
132 }
134 static void term_forward_char(void)
135 {
136 if (term_cmd_buf_index < term_cmd_buf_size) {
137 term_cmd_buf_index++;
138 }
139 }
141 static void term_delete_char(void)
142 {
143 if (term_cmd_buf_index < term_cmd_buf_size) {
144 memmove(term_cmd_buf + term_cmd_buf_index,
145 term_cmd_buf + term_cmd_buf_index + 1,
146 term_cmd_buf_size - term_cmd_buf_index - 1);
147 term_cmd_buf_size--;
148 }
149 }
151 static void term_backspace(void)
152 {
153 if (term_cmd_buf_index > 0) {
154 term_backward_char();
155 term_delete_char();
156 }
157 }
159 static void term_bol(void)
160 {
161 term_cmd_buf_index = 0;
162 }
164 static void term_eol(void)
165 {
166 term_cmd_buf_index = term_cmd_buf_size;
167 }
169 static void term_up_char(void)
170 {
171 int idx;
173 if (term_hist_entry == 0)
174 return;
175 if (term_hist_entry == -1) {
176 /* Find latest entry */
177 for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
178 if (term_history[idx] == NULL)
179 break;
180 }
181 term_hist_entry = idx;
182 }
183 term_hist_entry--;
184 if (term_hist_entry >= 0) {
185 pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
186 term_history[term_hist_entry]);
187 term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
188 }
189 }
191 static void term_down_char(void)
192 {
193 if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
194 return;
195 if (term_history[++term_hist_entry] != NULL) {
196 pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
197 term_history[term_hist_entry]);
198 } else {
199 term_hist_entry = -1;
200 }
201 term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
202 }
204 static void term_hist_add(const char *cmdline)
205 {
206 char *hist_entry, *new_entry;
207 int idx;
209 if (cmdline[0] == '\0')
210 return;
211 new_entry = NULL;
212 if (term_hist_entry != -1) {
213 /* We were editing an existing history entry: replace it */
214 hist_entry = term_history[term_hist_entry];
215 idx = term_hist_entry;
216 if (strcmp(hist_entry, cmdline) == 0) {
217 goto same_entry;
218 }
219 }
220 /* Search cmdline in history buffers */
221 for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
222 hist_entry = term_history[idx];
223 if (hist_entry == NULL)
224 break;
225 if (strcmp(hist_entry, cmdline) == 0) {
226 same_entry:
227 new_entry = hist_entry;
228 /* Put this entry at the end of history */
229 memmove(&term_history[idx], &term_history[idx + 1],
230 &term_history[TERM_MAX_CMDS] - &term_history[idx + 1]);
231 term_history[TERM_MAX_CMDS - 1] = NULL;
232 for (; idx < TERM_MAX_CMDS; idx++) {
233 if (term_history[idx] == NULL)
234 break;
235 }
236 break;
237 }
238 }
239 if (idx == TERM_MAX_CMDS) {
240 /* Need to get one free slot */
241 free(term_history[0]);
242 memcpy(term_history, &term_history[1],
243 &term_history[TERM_MAX_CMDS] - &term_history[1]);
244 term_history[TERM_MAX_CMDS - 1] = NULL;
245 idx = TERM_MAX_CMDS - 1;
246 }
247 if (new_entry == NULL)
248 new_entry = strdup(cmdline);
249 term_history[idx] = new_entry;
250 term_hist_entry = -1;
251 }
253 /* completion support */
255 void add_completion(const char *str)
256 {
257 if (nb_completions < NB_COMPLETIONS_MAX) {
258 completions[nb_completions++] = qemu_strdup(str);
259 }
260 }
262 static void term_completion(void)
263 {
264 int len, i, j, max_width, nb_cols;
265 char *cmdline;
267 nb_completions = 0;
269 cmdline = qemu_malloc(term_cmd_buf_index + 1);
270 if (!cmdline)
271 return;
272 memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
273 cmdline[term_cmd_buf_index] = '\0';
274 qemu_free(cmdline);
276 /* no completion found */
277 if (nb_completions <= 0)
278 return;
279 if (nb_completions == 1) {
280 len = strlen(completions[0]);
281 for(i = completion_index; i < len; i++) {
282 term_insert_char(completions[0][i]);
283 }
284 /* extra space for next argument. XXX: make it more generic */
285 if (len > 0 && completions[0][len - 1] != '/')
286 term_insert_char(' ');
287 } else {
288 term_printf("\n");
289 max_width = 0;
290 for(i = 0; i < nb_completions; i++) {
291 len = strlen(completions[i]);
292 if (len > max_width)
293 max_width = len;
294 }
295 max_width += 2;
296 if (max_width < 10)
297 max_width = 10;
298 else if (max_width > 80)
299 max_width = 80;
300 nb_cols = 80 / max_width;
301 j = 0;
302 for(i = 0; i < nb_completions; i++) {
303 term_printf("%-*s", max_width, completions[i]);
304 if (++j == nb_cols || i == (nb_completions - 1)) {
305 term_printf("\n");
306 j = 0;
307 }
308 }
309 term_show_prompt2();
310 }
311 }
313 /* return true if command handled */
314 void readline_handle_byte(int ch)
315 {
316 switch(term_esc_state) {
317 case IS_NORM:
318 switch(ch) {
319 case 1:
320 term_bol();
321 break;
322 case 4:
323 term_delete_char();
324 break;
325 case 5:
326 term_eol();
327 break;
328 case 9:
329 term_completion();
330 break;
331 case 10:
332 case 13:
333 term_cmd_buf[term_cmd_buf_size] = '\0';
334 if (!term_is_password)
335 term_hist_add(term_cmd_buf);
336 term_printf("\n");
337 /* NOTE: readline_start can be called here */
338 term_readline_func(term_readline_opaque, term_cmd_buf);
339 break;
340 case 27:
341 term_esc_state = IS_ESC;
342 break;
343 case 127:
344 case 8:
345 term_backspace();
346 break;
347 case 155:
348 term_esc_state = IS_CSI;
349 break;
350 default:
351 if (ch >= 32) {
352 term_insert_char(ch);
353 }
354 break;
355 }
356 break;
357 case IS_ESC:
358 if (ch == '[') {
359 term_esc_state = IS_CSI;
360 term_esc_param = 0;
361 } else {
362 term_esc_state = IS_NORM;
363 }
364 break;
365 case IS_CSI:
366 switch(ch) {
367 case 'A':
368 case 'F':
369 term_up_char();
370 break;
371 case 'B':
372 case 'E':
373 term_down_char();
374 break;
375 case 'D':
376 term_backward_char();
377 break;
378 case 'C':
379 term_forward_char();
380 break;
381 case '0' ... '9':
382 term_esc_param = term_esc_param * 10 + (ch - '0');
383 goto the_end;
384 case '~':
385 switch(term_esc_param) {
386 case 1:
387 term_bol();
388 break;
389 case 3:
390 term_delete_char();
391 break;
392 case 4:
393 term_eol();
394 break;
395 }
396 break;
397 default:
398 break;
399 }
400 term_esc_state = IS_NORM;
401 the_end:
402 break;
403 }
404 term_update();
405 }
407 void readline_start(const char *prompt, int is_password,
408 ReadLineFunc *readline_func, void *opaque)
409 {
410 pstrcpy(term_prompt, sizeof(term_prompt), prompt);
411 term_readline_func = readline_func;
412 term_readline_opaque = opaque;
413 term_is_password = is_password;
414 term_show_prompt();
415 }
417 const char *readline_get_history(unsigned int index)
418 {
419 if (index >= TERM_MAX_CMDS)
420 return NULL;
421 return term_history[index];
422 }