source: mainline/uspace/app/bdsh/input.c@ ed372da

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ed372da was ed372da, checked in by Jiri Svoboda <jiri@…>, 16 years ago

Add some comments.

  • Property mode set to 100644
File size: 14.3 KB
Line 
1/* Copyright (c) 2008, Tim Post <tinkertim@gmail.com>
2 * All rights reserved.
3 * Copyright (c) 2008, Jiri Svoboda - All Rights Reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * Neither the name of the original program's authors nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <io/console.h>
36#include <io/keycode.h>
37#include <io/style.h>
38#include <io/color.h>
39#include <vfs/vfs.h>
40#include <errno.h>
41#include <assert.h>
42#include <bool.h>
43
44#include "config.h"
45#include "util.h"
46#include "scli.h"
47#include "input.h"
48#include "errors.h"
49#include "exec.h"
50
51#define HISTORY_LEN 10
52
53/** Text input field. */
54typedef struct {
55 /** Buffer holding text currently being edited */
56 wchar_t buffer[INPUT_MAX];
57 /** Screen coordinates of the top-left corner of the text field */
58 int col0, row0;
59 /** Screen dimensions */
60 int con_cols, con_rows;
61 /** Number of characters in @c buffer */
62 int nc;
63 /** Caret position within buffer */
64 int pos;
65 /** Selection mark position within buffer */
66 int sel_start;
67
68 /** History (dynamically allocated strings) */
69 char *history[1 + HISTORY_LEN];
70 /** Number of entries in @c history, not counting [0] */
71 int hnum;
72 /** Current position in history */
73 int hpos;
74 /** Exit flag */
75 bool done;
76} tinput_t;
77
78/** Seek direction */
79typedef enum {
80 seek_backward = -1,
81 seek_forward = 1
82} seek_dir_t;
83
84static tinput_t tinput;
85
86static char *tinput_read(tinput_t *ti);
87static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb);
88static bool tinput_sel_active(tinput_t *ti);
89static void tinput_sel_delete(tinput_t *ti);
90static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev);
91static void tinput_key_shift(tinput_t *ti, console_event_t *ev);
92static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev);
93static void tinput_key_unmod(tinput_t *ti, console_event_t *ev);
94static void tinput_pre_seek(tinput_t *ti, bool shift_held);
95static void tinput_post_seek(tinput_t *ti, bool shift_held);
96
97/* Tokenizes input from console, sees if the first word is a built-in, if so
98 * invokes the built-in entry point (a[0]) passing all arguments in a[] to
99 * the handler */
100int tok_input(cliuser_t *usr)
101{
102 char *cmd[WORD_MAX];
103 int n = 0, i = 0;
104 int rc = 0;
105 char *tmp;
106
107 if (NULL == usr->line)
108 return CL_EFAIL;
109
110 tmp = str_dup(usr->line);
111
112 cmd[n] = strtok(tmp, " ");
113 while (cmd[n] && n < WORD_MAX) {
114 cmd[++n] = strtok(NULL, " ");
115 }
116
117 /* We have rubbish */
118 if (NULL == cmd[0]) {
119 rc = CL_ENOENT;
120 goto finit;
121 }
122
123 /* Its a builtin command ? */
124 if ((i = (is_builtin(cmd[0]))) > -1) {
125 rc = run_builtin(i, cmd, usr);
126 goto finit;
127 /* Its a module ? */
128 } else if ((i = (is_module(cmd[0]))) > -1) {
129 rc = run_module(i, cmd);
130 goto finit;
131 }
132
133 /* See what try_exec thinks of it */
134 rc = try_exec(cmd[0], cmd);
135
136finit:
137 if (NULL != usr->line) {
138 free(usr->line);
139 usr->line = (char *) NULL;
140 }
141 if (NULL != tmp)
142 free(tmp);
143
144 return rc;
145}
146
147static void tinput_display_tail(tinput_t *ti, int start, int pad)
148{
149 static wchar_t dbuf[INPUT_MAX + 1];
150 int sa, sb;
151 int i, p;
152
153 tinput_sel_get_bounds(ti, &sa, &sb);
154
155 console_goto(fphone(stdout), (ti->col0 + start) % ti->con_cols,
156 ti->row0 + (ti->col0 + start) / ti->con_cols);
157 console_set_color(fphone(stdout), COLOR_BLACK, COLOR_WHITE, 0);
158
159 p = start;
160 if (p < sa) {
161 memcpy(dbuf, ti->buffer + p, (sa - p) * sizeof(wchar_t));
162 dbuf[sa - p] = '\0';
163 printf("%ls", dbuf);
164 p = sa;
165 }
166
167 if (p < sb) {
168 fflush(stdout);
169 console_set_color(fphone(stdout), COLOR_BLACK, COLOR_RED, 0);
170 memcpy(dbuf, ti->buffer + p,
171 (sb - p) * sizeof(wchar_t));
172 dbuf[sb - p] = '\0';
173 printf("%ls", dbuf);
174 p = sb;
175 }
176
177 fflush(stdout);
178 console_set_color(fphone(stdout), COLOR_BLACK, COLOR_WHITE, 0);
179
180 if (p < ti->nc) {
181 memcpy(dbuf, ti->buffer + p,
182 (ti->nc - p) * sizeof(wchar_t));
183 dbuf[ti->nc - p] = '\0';
184 printf("%ls", dbuf);
185 }
186
187 for (i = 0; i < pad; ++i)
188 putchar(' ');
189 fflush(stdout);
190}
191
192static char *tinput_get_str(tinput_t *ti)
193{
194 return wstr_to_astr(ti->buffer);
195}
196
197static void tinput_position_caret(tinput_t *ti)
198{
199 console_goto(fphone(stdout), (ti->col0 + ti->pos) % ti->con_cols,
200 ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
201}
202
203/** Update row0 in case the screen could have scrolled. */
204static void tinput_update_origin(tinput_t *ti)
205{
206 int width, rows;
207
208 width = ti->col0 + ti->nc;
209 rows = (width / ti->con_cols) + 1;
210
211 /* Update row0 if the screen scrolled. */
212 if (ti->row0 + rows > ti->con_rows)
213 ti->row0 = ti->con_rows - rows;
214}
215
216static void tinput_insert_char(tinput_t *ti, wchar_t c)
217{
218 int i;
219 int new_width, new_height;
220
221 if (ti->nc == INPUT_MAX)
222 return;
223
224 new_width = ti->col0 + ti->nc + 1;
225 if (new_width % ti->con_cols == 0) {
226 /* Advancing to new line. */
227 new_height = (new_width / ti->con_cols) + 1;
228 if (new_height >= ti->con_rows)
229 return; /* Disallow text longer than 1 page for now. */
230 }
231
232 for (i = ti->nc; i > ti->pos; --i)
233 ti->buffer[i] = ti->buffer[i - 1];
234
235 ti->buffer[ti->pos] = c;
236 ti->pos += 1;
237 ti->nc += 1;
238 ti->buffer[ti->nc] = '\0';
239 ti->sel_start = ti->pos;
240
241 tinput_display_tail(ti, ti->pos - 1, 0);
242 tinput_update_origin(ti);
243 tinput_position_caret(ti);
244}
245
246static void tinput_backspace(tinput_t *ti)
247{
248 int i;
249
250 if (tinput_sel_active(ti)) {
251 tinput_sel_delete(ti);
252 return;
253 }
254
255 if (ti->pos == 0)
256 return;
257
258 for (i = ti->pos; i < ti->nc; ++i)
259 ti->buffer[i - 1] = ti->buffer[i];
260 ti->pos -= 1;
261 ti->nc -= 1;
262 ti->buffer[ti->nc] = '\0';
263 ti->sel_start = ti->pos;
264
265 tinput_display_tail(ti, ti->pos, 1);
266 tinput_position_caret(ti);
267}
268
269static void tinput_delete(tinput_t *ti)
270{
271 if (tinput_sel_active(ti)) {
272 tinput_sel_delete(ti);
273 return;
274 }
275
276 if (ti->pos == ti->nc)
277 return;
278
279 ti->pos += 1;
280 ti->sel_start = ti->pos;
281
282 tinput_backspace(ti);
283}
284
285static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir, bool shift_held)
286{
287 tinput_pre_seek(ti, shift_held);
288
289 if (dir == seek_forward) {
290 if (ti->pos < ti->nc)
291 ti->pos += 1;
292 } else {
293 if (ti->pos > 0)
294 ti->pos -= 1;
295 }
296
297 tinput_post_seek(ti, shift_held);
298}
299
300static void tinput_seek_word(tinput_t *ti, seek_dir_t dir, bool shift_held)
301{
302 tinput_pre_seek(ti, shift_held);
303
304 if (dir == seek_forward) {
305 if (ti->pos == ti->nc)
306 return;
307
308 while (1) {
309 ti->pos += 1;
310
311 if (ti->pos == ti->nc)
312 break;
313
314 if (ti->buffer[ti->pos - 1] == ' ' &&
315 ti->buffer[ti->pos] != ' ')
316 break;
317 }
318 } else {
319 if (ti->pos == 0)
320 return;
321
322 while (1) {
323 ti->pos -= 1;
324
325 if (ti->pos == 0)
326 break;
327
328 if (ti->buffer[ti->pos - 1] == ' ' &&
329 ti->buffer[ti->pos] != ' ')
330 break;
331 }
332
333 }
334
335 tinput_post_seek(ti, shift_held);
336}
337
338static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir, bool shift_held)
339{
340 tinput_pre_seek(ti, shift_held);
341
342 if (dir == seek_forward) {
343 if (ti->pos + ti->con_cols <= ti->nc)
344 ti->pos = ti->pos + ti->con_cols;
345 } else {
346 if (ti->pos - ti->con_cols >= 0)
347 ti->pos = ti->pos - ti->con_cols;
348 }
349
350 tinput_post_seek(ti, shift_held);
351}
352
353static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
354{
355 tinput_pre_seek(ti, shift_held);
356
357 if (dir == seek_backward)
358 ti->pos = 0;
359 else
360 ti->pos = ti->nc;
361
362 tinput_post_seek(ti, shift_held);
363}
364
365static void tinput_pre_seek(tinput_t *ti, bool shift_held)
366{
367 if (tinput_sel_active(ti) && !shift_held) {
368 /* Unselect and redraw. */
369 ti->sel_start = ti->pos;
370 tinput_display_tail(ti, 0, 0);
371 tinput_position_caret(ti);
372 }
373}
374
375static void tinput_post_seek(tinput_t *ti, bool shift_held)
376{
377 if (shift_held) {
378 /* Selecting text. Need redraw. */
379 tinput_display_tail(ti, 0, 0);
380 } else {
381 /* Shift not held. Keep selection empty. */
382 ti->sel_start = ti->pos;
383 }
384 tinput_position_caret(ti);
385}
386
387static void tinput_history_insert(tinput_t *ti, char *str)
388{
389 int i;
390
391 if (ti->hnum < HISTORY_LEN) {
392 ti->hnum += 1;
393 } else {
394 if (ti->history[HISTORY_LEN] != NULL)
395 free(ti->history[HISTORY_LEN]);
396 }
397
398 for (i = ti->hnum; i > 1; --i)
399 ti->history[i] = ti->history[i - 1];
400
401 ti->history[1] = str_dup(str);
402
403 if (ti->history[0] != NULL) {
404 free(ti->history[0]);
405 ti->history[0] = NULL;
406 }
407}
408
409static void tinput_set_str(tinput_t *ti, char *str)
410{
411 str_to_wstr(ti->buffer, INPUT_MAX, str);
412 ti->nc = wstr_length(ti->buffer);
413 ti->pos = ti->nc;
414 ti->sel_start = ti->pos;
415}
416
417static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb)
418{
419 if (ti->sel_start < ti->pos) {
420 *sa = ti->sel_start;
421 *sb = ti->pos;
422 } else {
423 *sa = ti->pos;
424 *sb = ti->sel_start;
425 }
426}
427
428static bool tinput_sel_active(tinput_t *ti)
429{
430 return ti->sel_start != ti->pos;
431}
432
433static void tinput_sel_delete(tinput_t *ti)
434{
435 int sa, sb;
436
437 tinput_sel_get_bounds(ti, &sa, &sb);
438 if (sa == sb)
439 return;
440
441 memmove(ti->buffer + sa, ti->buffer + sb,
442 (ti->nc - sb) * sizeof(wchar_t));
443 ti->pos = ti->sel_start = sa;
444 ti->nc -= (sb - sa);
445 ti->buffer[ti->nc] = '\0';
446
447 tinput_display_tail(ti, sa, sb - sa);
448 tinput_position_caret(ti);
449}
450
451static void tinput_history_seek(tinput_t *ti, int offs)
452{
453 int pad;
454
455 if (ti->hpos + offs < 0 || ti->hpos + offs > ti->hnum)
456 return;
457
458 if (ti->history[ti->hpos] != NULL) {
459 free(ti->history[ti->hpos]);
460 ti->history[ti->hpos] = NULL;
461 }
462
463 ti->history[ti->hpos] = tinput_get_str(ti);
464 ti->hpos += offs;
465
466 pad = ti->nc - str_length(ti->history[ti->hpos]);
467 if (pad < 0) pad = 0;
468
469 tinput_set_str(ti, ti->history[ti->hpos]);
470 tinput_display_tail(ti, 0, pad);
471 tinput_update_origin(ti);
472 tinput_position_caret(ti);
473}
474
475/** Initialize text input field.
476 *
477 * Must be called before using the field. It clears the history.
478 */
479static void tinput_init(tinput_t *ti)
480{
481 ti->hnum = 0;
482 ti->hpos = 0;
483 ti->history[0] = NULL;
484}
485
486/** Read in one line of input. */
487static char *tinput_read(tinput_t *ti)
488{
489 console_event_t ev;
490 char *str;
491
492 fflush(stdout);
493
494 if (console_get_size(fphone(stdin), &ti->con_cols, &ti->con_rows) != EOK)
495 return NULL;
496 if (console_get_pos(fphone(stdin), &ti->col0, &ti->row0) != EOK)
497 return NULL;
498
499 ti->pos = ti->sel_start = 0;
500 ti->nc = 0;
501 ti->buffer[0] = '\0';
502 ti->done = false;
503
504 while (!ti->done) {
505 fflush(stdout);
506 if (!console_get_event(fphone(stdin), &ev))
507 return NULL;
508
509 if (ev.type != KEY_PRESS)
510 continue;
511
512 if ((ev.mods & KM_CTRL) != 0 &&
513 (ev.mods & (KM_ALT | KM_SHIFT)) == 0) {
514 tinput_key_ctrl(ti, &ev);
515 }
516
517 if ((ev.mods & KM_SHIFT) != 0 &&
518 (ev.mods & (KM_CTRL | KM_ALT)) == 0) {
519 tinput_key_shift(ti, &ev);
520 }
521
522 if ((ev.mods & KM_CTRL) != 0 &&
523 (ev.mods & KM_SHIFT) != 0 &&
524 (ev.mods & KM_ALT) == 0) {
525 tinput_key_ctrl_shift(ti, &ev);
526 }
527
528 if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
529 tinput_key_unmod(ti, &ev);
530 }
531
532 if (ev.c >= ' ') {
533 tinput_sel_delete(ti);
534 tinput_insert_char(ti, ev.c);
535 }
536 }
537
538 ti->pos = ti->nc;
539 tinput_position_caret(ti);
540 putchar('\n');
541
542 str = tinput_get_str(ti);
543 if (str_cmp(str, "") != 0)
544 tinput_history_insert(ti, str);
545
546 ti->hpos = 0;
547
548 return str;
549}
550
551static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
552{
553 switch (ev->key) {
554 case KC_LEFT:
555 tinput_seek_word(ti, seek_backward, false);
556 break;
557 case KC_RIGHT:
558 tinput_seek_word(ti, seek_forward, false);
559 break;
560 case KC_UP:
561 tinput_seek_vertical(ti, seek_backward, false);
562 break;
563 case KC_DOWN:
564 tinput_seek_vertical(ti, seek_forward, false);
565 break;
566 default:
567 break;
568 }
569}
570
571static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
572{
573 switch (ev->key) {
574 case KC_LEFT:
575 tinput_seek_word(ti, seek_backward, true);
576 break;
577 case KC_RIGHT:
578 tinput_seek_word(ti, seek_forward, true);
579 break;
580 case KC_UP:
581 tinput_seek_vertical(ti, seek_backward, true);
582 break;
583 case KC_DOWN:
584 tinput_seek_vertical(ti, seek_forward, true);
585 break;
586 default:
587 break;
588 }
589}
590
591static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
592{
593 switch (ev->key) {
594 case KC_LEFT:
595 tinput_seek_cell(ti, seek_backward, true);
596 break;
597 case KC_RIGHT:
598 tinput_seek_cell(ti, seek_forward, true);
599 break;
600 case KC_UP:
601 tinput_seek_vertical(ti, seek_backward, true);
602 break;
603 case KC_DOWN:
604 tinput_seek_vertical(ti, seek_forward, true);
605 break;
606 case KC_HOME:
607 tinput_seek_max(ti, seek_backward, true);
608 break;
609 case KC_END:
610 tinput_seek_max(ti, seek_forward, true);
611 break;
612 default:
613 break;
614 }
615}
616
617static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
618{
619 switch (ev->key) {
620 case KC_ENTER:
621 case KC_NENTER:
622 ti->done = true;
623 break;
624 case KC_BACKSPACE:
625 tinput_backspace(ti);
626 break;
627 case KC_DELETE:
628 tinput_delete(ti);
629 break;
630 case KC_LEFT:
631 tinput_seek_cell(ti, seek_backward, false);
632 break;
633 case KC_RIGHT:
634 tinput_seek_cell(ti, seek_forward, false);
635 break;
636 case KC_HOME:
637 tinput_seek_max(ti, seek_backward, false);
638 break;
639 case KC_END:
640 tinput_seek_max(ti, seek_forward, false);
641 break;
642 case KC_UP:
643 tinput_history_seek(ti, +1);
644 break;
645 case KC_DOWN:
646 tinput_history_seek(ti, -1);
647 break;
648 default:
649 break;
650 }
651}
652
653void get_input(cliuser_t *usr)
654{
655 char *str;
656
657 fflush(stdout);
658 console_set_style(fphone(stdout), STYLE_EMPHASIS);
659 printf("%s", usr->prompt);
660 fflush(stdout);
661 console_set_style(fphone(stdout), STYLE_NORMAL);
662
663 str = tinput_read(&tinput);
664
665 /* Check for empty input. */
666 if (str_cmp(str, "") == 0) {
667 free(str);
668 return;
669 }
670
671 usr->line = str;
672 return;
673}
674
675void input_init(void)
676{
677 tinput_init(&tinput);
678}
Note: See TracBrowser for help on using the repository browser.