source: mainline/uspace/app/bdsh/input.c@ 3041fef1

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

Change boundary behavior of vertical seek: keep column number, do not push horizontally to beginning/end of text.

  • Property mode set to 100644
File size: 9.4 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 <vfs/vfs.h>
39#include <errno.h>
40#include <assert.h>
41#include <bool.h>
42
43#include "config.h"
44#include "util.h"
45#include "scli.h"
46#include "input.h"
47#include "errors.h"
48#include "exec.h"
49
50#define HISTORY_LEN 10
51
52typedef struct {
53 wchar_t buffer[INPUT_MAX];
54 int col0, row0;
55 int con_cols, con_rows;
56 int nc;
57 int pos;
58
59 char *history[1 + HISTORY_LEN];
60 int hnum;
61 int hpos;
62} tinput_t;
63
64typedef enum {
65 seek_backward = -1,
66 seek_forward = 1
67} seek_dir_t;
68
69static tinput_t tinput;
70
71static char *tinput_read(tinput_t *ti);
72
73/* Tokenizes input from console, sees if the first word is a built-in, if so
74 * invokes the built-in entry point (a[0]) passing all arguments in a[] to
75 * the handler */
76int tok_input(cliuser_t *usr)
77{
78 char *cmd[WORD_MAX];
79 int n = 0, i = 0;
80 int rc = 0;
81 char *tmp;
82
83 if (NULL == usr->line)
84 return CL_EFAIL;
85
86 tmp = str_dup(usr->line);
87
88 cmd[n] = strtok(tmp, " ");
89 while (cmd[n] && n < WORD_MAX) {
90 cmd[++n] = strtok(NULL, " ");
91 }
92
93 /* We have rubbish */
94 if (NULL == cmd[0]) {
95 rc = CL_ENOENT;
96 goto finit;
97 }
98
99 /* Its a builtin command ? */
100 if ((i = (is_builtin(cmd[0]))) > -1) {
101 rc = run_builtin(i, cmd, usr);
102 goto finit;
103 /* Its a module ? */
104 } else if ((i = (is_module(cmd[0]))) > -1) {
105 rc = run_module(i, cmd);
106 goto finit;
107 }
108
109 /* See what try_exec thinks of it */
110 rc = try_exec(cmd[0], cmd);
111
112finit:
113 if (NULL != usr->line) {
114 free(usr->line);
115 usr->line = (char *) NULL;
116 }
117 if (NULL != tmp)
118 free(tmp);
119
120 return rc;
121}
122
123static void tinput_display_tail(tinput_t *ti, int start, int pad)
124{
125 int i;
126
127 console_goto(fphone(stdout), (ti->col0 + start) % ti->con_cols,
128 ti->row0 + (ti->col0 + start) / ti->con_cols);
129 printf("%ls", ti->buffer + start);
130 for (i = 0; i < pad; ++i)
131 putchar(' ');
132 fflush(stdout);
133}
134
135static char *tinput_get_str(tinput_t *ti)
136{
137 return wstr_to_astr(ti->buffer);
138}
139
140static void tinput_position_caret(tinput_t *ti)
141{
142 console_goto(fphone(stdout), (ti->col0 + ti->pos) % ti->con_cols,
143 ti->row0 + (ti->col0 + ti->pos) / ti->con_cols);
144}
145
146/** Update row0 in case the screen could have scrolled. */
147static void tinput_update_origin(tinput_t *ti)
148{
149 int width, rows;
150
151 width = ti->col0 + ti->nc;
152 rows = (width / ti->con_cols) + 1;
153
154 /* Update row0 if the screen scrolled. */
155 if (ti->row0 + rows > ti->con_rows)
156 ti->row0 = ti->con_rows - rows;
157}
158
159static void tinput_insert_char(tinput_t *ti, wchar_t c)
160{
161 int i;
162 int new_width, new_height;
163
164 if (ti->nc == INPUT_MAX)
165 return;
166
167 new_width = ti->col0 + ti->nc + 1;
168 if (new_width % ti->con_cols == 0) {
169 /* Advancing to new line. */
170 new_height = (new_width / ti->con_cols) + 1;
171 if (new_height >= ti->con_rows)
172 return; /* Disallow text longer than 1 page for now. */
173 }
174
175 for (i = ti->nc; i > ti->pos; --i)
176 ti->buffer[i] = ti->buffer[i - 1];
177
178 ti->buffer[ti->pos] = c;
179 ti->pos += 1;
180 ti->nc += 1;
181 ti->buffer[ti->nc] = '\0';
182
183 tinput_display_tail(ti, ti->pos - 1, 0);
184 tinput_update_origin(ti);
185 tinput_position_caret(ti);
186}
187
188static void tinput_backspace(tinput_t *ti)
189{
190 int i;
191
192 if (ti->pos == 0)
193 return;
194
195 for (i = ti->pos; i < ti->nc; ++i)
196 ti->buffer[i - 1] = ti->buffer[i];
197 ti->pos -= 1;
198 ti->nc -= 1;
199 ti->buffer[ti->nc] = '\0';
200
201 tinput_display_tail(ti, ti->pos, 1);
202 tinput_position_caret(ti);
203}
204
205static void tinput_delete(tinput_t *ti)
206{
207 if (ti->pos == ti->nc)
208 return;
209
210 ti->pos += 1;
211 tinput_backspace(ti);
212}
213
214static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir)
215{
216 if (dir == seek_forward) {
217 if (ti->pos < ti->nc)
218 ti->pos += 1;
219 } else {
220 if (ti->pos > 0)
221 ti->pos -= 1;
222 }
223
224 tinput_position_caret(ti);
225}
226
227static void tinput_seek_word(tinput_t *ti, seek_dir_t dir)
228{
229 if (dir == seek_forward) {
230 if (ti->pos == ti->nc)
231 return;
232
233 while (1) {
234 ti->pos += 1;
235
236 if (ti->pos == ti->nc)
237 break;
238
239 if (ti->buffer[ti->pos - 1] == ' ' &&
240 ti->buffer[ti->pos] != ' ')
241 break;
242 }
243 } else {
244 if (ti->pos == 0)
245 return;
246
247 while (1) {
248 ti->pos -= 1;
249
250 if (ti->pos == 0)
251 break;
252
253 if (ti->buffer[ti->pos - 1] == ' ' &&
254 ti->buffer[ti->pos] != ' ')
255 break;
256 }
257
258 }
259
260 tinput_position_caret(ti);
261}
262
263static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir)
264{
265 if (dir == seek_forward) {
266 if (ti->pos + ti->con_cols <= ti->nc)
267 ti->pos = ti->pos + ti->con_cols;
268 } else {
269 if (ti->pos - ti->con_cols >= 0)
270 ti->pos = ti->pos - ti->con_cols;
271 }
272
273 tinput_position_caret(ti);
274}
275
276static void tinput_seek_max(tinput_t *ti, seek_dir_t dir)
277{
278 if (dir == seek_backward)
279 ti->pos = 0;
280 else
281 ti->pos = ti->nc;
282
283 tinput_position_caret(ti);
284}
285
286static void tinput_history_insert(tinput_t *ti, char *str)
287{
288 int i;
289
290 if (ti->hnum < HISTORY_LEN) {
291 ti->hnum += 1;
292 } else {
293 if (ti->history[HISTORY_LEN] != NULL)
294 free(ti->history[HISTORY_LEN]);
295 }
296
297 for (i = ti->hnum; i > 1; --i)
298 ti->history[i] = ti->history[i - 1];
299
300 ti->history[1] = str_dup(str);
301
302 if (ti->history[0] != NULL) {
303 free(ti->history[0]);
304 ti->history[0] = NULL;
305 }
306}
307
308static void tinput_set_str(tinput_t *ti, char *str)
309{
310 str_to_wstr(ti->buffer, INPUT_MAX, str);
311 ti->nc = wstr_length(ti->buffer);
312 ti->pos = ti->nc;
313}
314
315static void tinput_history_seek(tinput_t *ti, int offs)
316{
317 int pad;
318
319 if (ti->hpos + offs < 0 || ti->hpos + offs > ti->hnum)
320 return;
321
322 if (ti->history[ti->hpos] != NULL) {
323 free(ti->history[ti->hpos]);
324 ti->history[ti->hpos] = NULL;
325 }
326
327 ti->history[ti->hpos] = tinput_get_str(ti);
328 ti->hpos += offs;
329
330 pad = ti->nc - str_length(ti->history[ti->hpos]);
331 if (pad < 0) pad = 0;
332
333 tinput_set_str(ti, ti->history[ti->hpos]);
334 tinput_display_tail(ti, 0, pad);
335 tinput_update_origin(ti);
336 tinput_position_caret(ti);
337}
338
339static void tinput_init(tinput_t *ti)
340{
341 ti->hnum = 0;
342 ti->hpos = 0;
343 ti->history[0] = NULL;
344}
345
346static char *tinput_read(tinput_t *ti)
347{
348 console_event_t ev;
349 char *str;
350
351 fflush(stdout);
352
353 if (console_get_size(fphone(stdin), &ti->con_cols, &ti->con_rows) != EOK)
354 return NULL;
355 if (console_get_pos(fphone(stdin), &ti->col0, &ti->row0) != EOK)
356 return NULL;
357
358 ti->pos = 0;
359 ti->nc = 0;
360 ti->buffer[0] = '\0';
361
362 while (true) {
363 fflush(stdout);
364 if (!console_get_event(fphone(stdin), &ev))
365 return NULL;
366
367 if (ev.type != KEY_PRESS)
368 continue;
369
370 if ((ev.mods & KM_CTRL) != 0 &&
371 (ev.mods & (KM_ALT | KM_SHIFT)) == 0) {
372 switch (ev.key) {
373 case KC_LEFT:
374 tinput_seek_word(ti, seek_backward);
375 break;
376 case KC_RIGHT:
377 tinput_seek_word(ti, seek_forward);
378 break;
379 case KC_UP:
380 tinput_seek_vertical(ti, seek_backward);
381 break;
382 case KC_DOWN:
383 tinput_seek_vertical(ti, seek_forward);
384 break;
385 }
386 }
387
388 if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
389 switch (ev.key) {
390 case KC_ENTER:
391 case KC_NENTER:
392 goto done;
393 case KC_BACKSPACE:
394 tinput_backspace(ti);
395 break;
396 case KC_DELETE:
397 tinput_delete(ti);
398 break;
399 case KC_LEFT:
400 tinput_seek_cell(ti, seek_backward);
401 break;
402 case KC_RIGHT:
403 tinput_seek_cell(ti, seek_forward);
404 break;
405 case KC_HOME:
406 tinput_seek_max(ti, seek_backward);
407 break;
408 case KC_END:
409 tinput_seek_max(ti, seek_forward);
410 break;
411 case KC_UP:
412 tinput_history_seek(ti, +1);
413 break;
414 case KC_DOWN:
415 tinput_history_seek(ti, -1);
416 break;
417 }
418 }
419
420 if (ev.c >= ' ') {
421 tinput_insert_char(ti, ev.c);
422 }
423 }
424
425done:
426 ti->pos = ti->nc;
427 tinput_position_caret(ti);
428 putchar('\n');
429
430 str = tinput_get_str(ti);
431 if (str_cmp(str, "") != 0)
432 tinput_history_insert(ti, str);
433
434 ti->hpos = 0;
435
436 return str;
437}
438
439void get_input(cliuser_t *usr)
440{
441 char *str;
442
443 fflush(stdout);
444 console_set_style(fphone(stdout), STYLE_EMPHASIS);
445 printf("%s", usr->prompt);
446 fflush(stdout);
447 console_set_style(fphone(stdout), STYLE_NORMAL);
448
449 str = tinput_read(&tinput);
450
451 /* Check for empty input. */
452 if (str_cmp(str, "") == 0) {
453 free(str);
454 return;
455 }
456
457 usr->line = str;
458 return;
459}
460
461void input_init(void)
462{
463 tinput_init(&tinput);
464}
Note: See TracBrowser for help on using the repository browser.