source: mainline/kernel/generic/src/console/kconsole.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 19.2 KB
RevLine 
[2677758]1/*
[df4ed85]2 * Copyright (c) 2005 Jakub Jermar
[2677758]3 * 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
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[174156fd]29/** @addtogroup kernel_generic_console
[b45c443]30 * @{
31 */
32
[cf26ba9]33/**
[d6c8ff6]34 * @file kconsole.c
35 * @brief Kernel console.
[cf26ba9]36 *
37 * This file contains kernel thread managing the kernel console.
[d6c8ff6]38 *
[cf26ba9]39 */
40
[63e27ef]41#include <assert.h>
[f4338d2]42#include <console/kconsole.h>
[2677758]43#include <console/console.h>
44#include <console/chardev.h>
[442d0ae]45#include <console/cmd.h>
[f0d7bd9]46#include <console/prompt.h>
[bab75df6]47#include <stdio.h>
[ff3b3197]48#include <panic.h>
[d99c1d2]49#include <typedefs.h>
[5c9a08b]50#include <adt/list.h>
[ff3b3197]51#include <arch.h>
52#include <macros.h>
[f4338d2]53#include <debug.h>
[b2e121a]54#include <halt.h>
[19f857a]55#include <str.h>
[3ad953c]56#include <sysinfo/sysinfo.h>
[e2b762ec]57#include <symtab.h>
[e16e0d59]58#include <errno.h>
[eec616b]59#include <putchar.h>
[aafed15]60#include <stdlib.h>
[e2b762ec]61
[ff3b3197]62/** Simple kernel console.
63 *
64 * The console is realized by kernel thread kconsole.
[f4338d2]65 * It doesn't understand any useful command on its own,
66 * but makes it possible for other kernel subsystems to
[ff3b3197]67 * register their own commands.
68 */
[d6c8ff6]69
[ff3b3197]70/** Locking.
71 *
72 * There is a list of cmd_info_t structures. This list
73 * is protected by cmd_lock spinlock. Note that specially
74 * the link elements of cmd_info_t are protected by
75 * this lock.
76 *
77 * Each cmd_info_t also has its own lock, which protects
78 * all elements thereof except the link element.
79 *
80 * cmd_lock must be acquired before any cmd_info lock.
81 * When locking two cmd info structures, structure with
82 * lower address must be locked first.
83 */
84
[d6c8ff6]85SPINLOCK_INITIALIZE(cmd_lock); /**< Lock protecting command list. */
[55b77d9]86LIST_INITIALIZE(cmd_list); /**< Command list. */
[d6c8ff6]87
[3bacee1]88static wchar_t history[KCONSOLE_HISTORY][MAX_CMDLINE] = { };
[98000fb]89static size_t history_pos = 0;
[ff3b3197]90
[3ad953c]91/** Initialize kconsole data structures
92 *
93 * This is the most basic initialization, almost no
94 * other kernel subsystem is ready yet.
95 *
96 */
[ff3b3197]97void kconsole_init(void)
98{
[3ad953c]99 unsigned int i;
[a35b458]100
[442d0ae]101 cmd_init();
[cf5ddf6]102 for (i = 0; i < KCONSOLE_HISTORY; i++)
[d6c8ff6]103 history[i][0] = 0;
[ff3b3197]104}
105
106/** Register kconsole command.
107 *
108 * @param cmd Structure describing the command.
109 *
[d6c8ff6]110 * @return False on failure, true on success.
111 *
[ff3b3197]112 */
[d6c8ff6]113bool cmd_register(cmd_info_t *cmd)
[ff3b3197]114{
115 spinlock_lock(&cmd_lock);
[a35b458]116
[ff3b3197]117 /*
118 * Make sure the command is not already listed.
119 */
[feeac0d]120 list_foreach(cmd_list, link, cmd_info_t, hlp) {
[ff3b3197]121 if (hlp == cmd) {
122 /* The command is already there. */
123 spinlock_unlock(&cmd_lock);
[d6c8ff6]124 return false;
[ff3b3197]125 }
[a35b458]126
[ff3b3197]127 /* Avoid deadlock. */
128 if (hlp < cmd) {
129 spinlock_lock(&hlp->lock);
130 spinlock_lock(&cmd->lock);
131 } else {
132 spinlock_lock(&cmd->lock);
133 spinlock_lock(&hlp->lock);
134 }
[a35b458]135
[d6c8ff6]136 if (str_cmp(hlp->name, cmd->name) == 0) {
[ff3b3197]137 /* The command is already there. */
138 spinlock_unlock(&hlp->lock);
139 spinlock_unlock(&cmd->lock);
140 spinlock_unlock(&cmd_lock);
[d6c8ff6]141 return false;
[ff3b3197]142 }
[a35b458]143
[ff3b3197]144 spinlock_unlock(&hlp->lock);
145 spinlock_unlock(&cmd->lock);
146 }
[a35b458]147
[ff3b3197]148 /*
149 * Now the command can be added.
150 */
[55b77d9]151 list_append(&cmd->link, &cmd_list);
[a35b458]152
[ff3b3197]153 spinlock_unlock(&cmd_lock);
[d6c8ff6]154 return true;
[ff3b3197]155}
[2677758]156
[07bd114e]157/** Print count times a character */
[7a0359b]158NO_TRACE static void print_cc(wchar_t ch, size_t count)
[0c8e692]159{
[98000fb]160 size_t i;
[cf5ddf6]161 for (i = 0; i < count; i++)
[ed88c8e]162 putwchar(ch);
[0c8e692]163}
164
[2cf87e50]165/** Try to find a command beginning with prefix */
[3266412]166const char *cmdtab_enum(const char *name, const char **h, void **ctx)
[0c8e692]167{
[3bacee1]168 link_t **startpos = (link_t **) ctx;
[08e103d4]169 size_t namelen = str_code_points(name);
[a35b458]170
[0c8e692]171 spinlock_lock(&cmd_lock);
[a35b458]172
[d6c8ff6]173 if (*startpos == NULL)
[55b77d9]174 *startpos = cmd_list.head.next;
[a35b458]175
[55b77d9]176 for (; *startpos != &cmd_list.head; *startpos = (*startpos)->next) {
[d6c8ff6]177 cmd_info_t *hlp = list_get_instance(*startpos, cmd_info_t, link);
[a35b458]178
[d6c8ff6]179 const char *curname = hlp->name;
[08e103d4]180 if (str_code_points(curname) < namelen)
[0c8e692]181 continue;
[a35b458]182
[d6c8ff6]183 if (str_lcmp(curname, name, namelen) == 0) {
[36b0490e]184 *startpos = (*startpos)->next;
[e98f1c3e]185 if (h)
[36b0490e]186 *h = hlp->description;
[a35b458]187
[d6c8ff6]188 spinlock_unlock(&cmd_lock);
[08e103d4]189 return (curname + str_lbytes(curname, namelen));
[0c8e692]190 }
191 }
[a35b458]192
[d6c8ff6]193 spinlock_unlock(&cmd_lock);
[0c8e692]194 return NULL;
195}
196
[d6c8ff6]197/** Command completion of the commands
198 *
199 * @param name String to match, changed to hint on exit
200 * @param size Input buffer size
201 *
202 * @return Number of found matches
[0c8e692]203 *
204 */
[36b0490e]205NO_TRACE static int cmdtab_compl(char *input, size_t size, indev_t *indev,
206 hints_enum_func_t hints_enum)
[0c8e692]207{
[d6c8ff6]208 const char *name = input;
[a35b458]209
[98000fb]210 size_t found = 0;
[a35b458]211
[e435537]212 /*
213 * Maximum Match Length: Length of longest matching common
214 * substring in case more than one match is found.
215 */
[1e01a35]216 size_t max_match_len = size;
217 size_t max_match_len_tmp = size;
[36b0490e]218 void *pos = NULL;
[d6c8ff6]219 const char *hint;
[36b0490e]220 const char *help;
[1e01a35]221 size_t hints_to_show = MAX_TAB_HINTS - 1;
222 size_t total_hints_shown = 0;
[f0d7bd9]223 bool continue_showing_hints = true;
[a35b458]224
[4f3aa76]225 char *output = malloc(MAX_CMDLINE);
226 if (!output) {
227 // TODO: fix the function so that it does not need allocation
228 printf("Can't complete command, out of memory.\n");
229 return 0;
230 }
231
[d6c8ff6]232 output[0] = 0;
[a35b458]233
[36b0490e]234 while ((hint = hints_enum(name, NULL, &pos))) {
[08e103d4]235 if ((found == 0) || (str_code_points(hint) > str_code_points(output)))
[f4b1535]236 str_cpy(output, MAX_CMDLINE, hint);
[a35b458]237
[0c8e692]238 found++;
239 }
[a35b458]240
[e435537]241 /*
242 * If the number of possible completions is more than MAX_TAB_HINTS,
243 * ask the user whether to display them or not.
244 */
[1e01a35]245 if (found > MAX_TAB_HINTS) {
[aca4a04]246 printf("\n");
[e435537]247 continue_showing_hints =
248 console_prompt_display_all_hints(indev, found);
[1e01a35]249 }
[a35b458]250
[08e103d4]251 if ((found > 1) && (str_code_points(output) != 0)) {
[0c8e692]252 printf("\n");
[d6c8ff6]253 pos = NULL;
[36b0490e]254 while ((hint = hints_enum(name, &help, &pos))) {
[a35b458]255
[f0d7bd9]256 if (continue_showing_hints) {
[36b0490e]257 if (help)
258 printf("%s%s (%s)\n", name, hint, help);
259 else
260 printf("%s%s\n", name, hint);
[a35b458]261
[1e01a35]262 --hints_to_show;
263 ++total_hints_shown;
[a35b458]264
[e435537]265 if ((hints_to_show == 0) && (total_hints_shown != found)) {
266 /* Ask user to continue */
267 continue_showing_hints =
268 console_prompt_more_hints(indev, &hints_to_show);
[1e01a35]269 }
270 }
[a35b458]271
[84239b1]272 max_match_len_tmp = 0;
273 while ((output[max_match_len_tmp] ==
[36b0490e]274 hint[max_match_len_tmp]) &&
[84239b1]275 (max_match_len_tmp < max_match_len))
276 ++max_match_len_tmp;
[a35b458]277
[1e01a35]278 max_match_len = max_match_len_tmp;
[0c8e692]279 }
[a35b458]280
[e435537]281 /* Keep only the characters common in all completions */
[1e01a35]282 output[max_match_len] = 0;
[0c8e692]283 }
[a35b458]284
[d6c8ff6]285 if (found > 0)
[f4b1535]286 str_cpy(input, size, output);
[a35b458]287
[a7199c2]288 free(output);
[0c8e692]289 return found;
290}
291
[6a75c134]292NO_TRACE static cmd_info_t *parse_cmd(const wchar_t *cmdline)
293{
294 size_t start = 0;
295 size_t end;
296 char *tmp;
[a35b458]297
[6a75c134]298 while (isspace(cmdline[start]))
299 start++;
[a35b458]300
[6a75c134]301 end = start + 1;
[a35b458]302
[6a75c134]303 while (!isspace(cmdline[end]))
304 end++;
[a35b458]305
[11b285d]306 tmp = malloc(STR_BOUNDS(end - start + 1));
[7473807]307 if (!tmp)
308 return NULL;
[a35b458]309
[6a75c134]310 wstr_to_str(tmp, end - start + 1, &cmdline[start]);
[a35b458]311
[6a75c134]312 spinlock_lock(&cmd_lock);
[a35b458]313
[6a75c134]314 list_foreach(cmd_list, link, cmd_info_t, hlp) {
315 spinlock_lock(&hlp->lock);
[a35b458]316
[6a75c134]317 if (str_cmp(hlp->name, tmp) == 0) {
318 spinlock_unlock(&hlp->lock);
319 spinlock_unlock(&cmd_lock);
320 free(tmp);
321 return hlp;
322 }
[a35b458]323
[6a75c134]324 spinlock_unlock(&hlp->lock);
325 }
[a35b458]326
[6a75c134]327 free(tmp);
328 spinlock_unlock(&cmd_lock);
[a35b458]329
[6a75c134]330 return NULL;
331}
332
[4f3aa76]333NO_TRACE static wchar_t *clever_readline(const char *prompt, indev_t *indev,
334 char *tmp)
[0c8e692]335{
336 printf("%s> ", prompt);
[a35b458]337
[98000fb]338 size_t position = 0;
[d6c8ff6]339 wchar_t *current = history[history_pos];
340 current[0] = 0;
[a35b458]341
[d6c8ff6]342 while (true) {
[44b7783]343 wchar_t ch = indev_pop_character(indev);
[a35b458]344
[d6c8ff6]345 if (ch == '\n') {
346 /* Enter */
[ed88c8e]347 putwchar(ch);
[0c8e692]348 break;
[5d67baa]349 }
[a35b458]350
[d6c8ff6]351 if (ch == '\b') {
352 /* Backspace */
[0c8e692]353 if (position == 0)
354 continue;
[a35b458]355
[d6c8ff6]356 if (wstr_remove(current, position - 1)) {
357 position--;
[ed88c8e]358 putwchar('\b');
[c8bf88d]359 printf("%ls ", current + position);
[08e103d4]360 print_cc('\b', wstr_code_points(current) - position + 1);
[d6c8ff6]361 continue;
362 }
[0c8e692]363 }
[a35b458]364
[d6c8ff6]365 if (ch == '\t') {
366 /* Tab completion */
[a35b458]367
[0c8e692]368 /* Move to the end of the word */
[d6c8ff6]369 for (; (current[position] != 0) && (!isspace(current[position]));
[cf5ddf6]370 position++)
[ed88c8e]371 putwchar(current[position]);
[a35b458]372
[e435537]373 /*
374 * Find the beginning of the word
375 * and copy it to tmp
376 */
[98000fb]377 size_t beg;
[d9ffc54]378 unsigned narg = 0;
[6b00876]379 if (position == 0) {
380 tmp[0] = '\0';
381 beg = 0;
[d9ffc54]382 } else {
[84239b1]383 beg = position - 1;
384 while ((beg > 0) && (!isspace(current[beg])))
[3bacee1]385 beg--;
[a35b458]386
[6b00876]387 if (isspace(current[beg]))
388 beg++;
[a35b458]389
[6b00876]390 wstr_to_str(tmp, position - beg + 1, current + beg);
391 }
[a35b458]392
[0cfc18d3]393 /* Count which argument number are we tabbing (narg=0 is cmd) */
[d9ffc54]394 bool sp = false;
[0cfc18d3]395 for (; beg > 0; beg--) {
396 if (isspace(current[beg])) {
397 if (!sp) {
398 narg++;
[d9ffc54]399 sp = true;
[0cfc18d3]400 }
[d9ffc54]401 } else
402 sp = false;
[0cfc18d3]403 }
[a35b458]404
[0cfc18d3]405 if (narg && isspace(current[0]))
406 narg--;
[a35b458]407
[d6c8ff6]408 int found;
[0cfc18d3]409 if (narg == 0) {
[d6c8ff6]410 /* Command completion */
[6a75c134]411 found = cmdtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE), indev,
412 cmdtab_enum);
[d6c8ff6]413 } else {
[6a75c134]414 /* Arguments completion */
415 cmd_info_t *cmd = parse_cmd(current);
[0cfc18d3]416 if (!cmd || !cmd->hints_enum || cmd->argc < narg)
[6a75c134]417 continue;
418 found = cmdtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE), indev,
419 cmd->hints_enum);
[0c8e692]420 }
[a35b458]421
[d6c8ff6]422 if (found == 0)
[0c8e692]423 continue;
[1e01a35]424
[e435537]425 /*
426 * We have hints, possibly many. In case of more than one hint,
427 * tmp will contain the common prefix.
428 */
[1e01a35]429 size_t off = 0;
430 size_t i = 0;
431 while ((ch = str_decode(tmp, &off, STR_NO_LIMIT)) != 0) {
432 if (!wstr_linsert(current, ch, position + i, MAX_CMDLINE))
433 break;
[a35b458]434
[1e01a35]435 i++;
436 }
[a35b458]437
[37c312a]438 if (found > 1) {
439 /* No unique hint, list was printed */
440 printf("%s> ", prompt);
441 printf("%ls", current);
[08e103d4]442 position += str_code_points(tmp);
443 print_cc('\b', wstr_code_points(current) - position);
[37c312a]444 continue;
445 }
[a35b458]446
[37c312a]447 /* We have a hint */
[a35b458]448
[37c312a]449 printf("%ls", current + position);
[08e103d4]450 position += str_code_points(tmp);
451 print_cc('\b', wstr_code_points(current) - position);
[a35b458]452
[08e103d4]453 if (position == wstr_code_points(current)) {
[37c312a]454 /* Insert a space after the last completed argument */
455 if (wstr_linsert(current, ' ', position, MAX_CMDLINE)) {
456 printf("%ls", current + position);
457 position++;
[0c8e692]458 }
459 }
460 continue;
461 }
[a35b458]462
[c8bf88d]463 if (ch == U_LEFT_ARROW) {
464 /* Left */
465 if (position > 0) {
[ed88c8e]466 putwchar('\b');
[c8bf88d]467 position--;
468 }
469 continue;
470 }
[a35b458]471
[c8bf88d]472 if (ch == U_RIGHT_ARROW) {
473 /* Right */
[08e103d4]474 if (position < wstr_code_points(current)) {
[ed88c8e]475 putwchar(current[position]);
[c8bf88d]476 position++;
477 }
478 continue;
479 }
[a35b458]480
[c8bf88d]481 if ((ch == U_UP_ARROW) || (ch == U_DOWN_ARROW)) {
482 /* Up, down */
483 print_cc('\b', position);
[08e103d4]484 print_cc(' ', wstr_code_points(current));
485 print_cc('\b', wstr_code_points(current));
[a35b458]486
[c8bf88d]487 if (ch == U_UP_ARROW) {
488 /* Up */
489 if (history_pos == 0)
490 history_pos = KCONSOLE_HISTORY - 1;
491 else
492 history_pos--;
493 } else {
494 /* Down */
495 history_pos++;
496 history_pos = history_pos % KCONSOLE_HISTORY;
497 }
498 current = history[history_pos];
499 printf("%ls", current);
[08e103d4]500 position = wstr_code_points(current);
[c8bf88d]501 continue;
502 }
[a35b458]503
[c8bf88d]504 if (ch == U_HOME_ARROW) {
505 /* Home */
506 print_cc('\b', position);
507 position = 0;
508 continue;
509 }
[a35b458]510
[c8bf88d]511 if (ch == U_END_ARROW) {
512 /* End */
513 printf("%ls", current + position);
[08e103d4]514 position = wstr_code_points(current);
[c8bf88d]515 continue;
516 }
[a35b458]517
[c8bf88d]518 if (ch == U_DELETE) {
519 /* Delete */
[08e103d4]520 if (position == wstr_code_points(current))
[0c8e692]521 continue;
[a35b458]522
[c8bf88d]523 if (wstr_remove(current, position)) {
524 printf("%ls ", current + position);
[08e103d4]525 print_cc('\b', wstr_code_points(current) - position + 1);
[0c8e692]526 }
527 continue;
528 }
[a35b458]529
[d6c8ff6]530 if (wstr_linsert(current, ch, position, MAX_CMDLINE)) {
531 printf("%ls", current + position);
532 position++;
[08e103d4]533 print_cc('\b', wstr_code_points(current) - position);
[d6c8ff6]534 }
[ba276f7]535 }
[a35b458]536
[08e103d4]537 if (wstr_code_points(current) > 0) {
[d6c8ff6]538 history_pos++;
539 history_pos = history_pos % KCONSOLE_HISTORY;
[2677758]540 }
[a35b458]541
[d6c8ff6]542 return current;
[2677758]543}
[ff3b3197]544
[d6c8ff6]545bool kconsole_check_poll(void)
[76fca31]546{
[d6c8ff6]547 return check_poll(stdin);
[76fca31]548}
549
[7a0359b]550NO_TRACE static bool parse_int_arg(const char *text, size_t len,
[96b02eb9]551 sysarg_t *result)
[91c78c9]552{
553 bool isaddr = false;
[e5fcf00]554 bool isptr = false;
[a35b458]555
[91c78c9]556 /* If we get a name, try to find it in symbol table */
[d0da921]557 if (text[0] == '&') {
558 isaddr = true;
[cf5ddf6]559 text++;
560 len--;
[d0da921]561 } else if (text[0] == '*') {
562 isptr = true;
[cf5ddf6]563 text++;
564 len--;
[d0da921]565 }
[a35b458]566
[d6c8ff6]567 if ((text[0] < '0') || (text[0] > '9')) {
568 char symname[MAX_SYMBOL_NAME];
[f4b1535]569 str_ncpy(symname, MAX_SYMBOL_NAME, text, len + 1);
[a35b458]570
[d6c8ff6]571 uintptr_t symaddr;
[b7fd2a0]572 errno_t rc = symtab_addr_lookup(symname, &symaddr);
[e16e0d59]573 switch (rc) {
574 case ENOENT:
[cf5ddf6]575 printf("Symbol %s not found.\n", symname);
[d6c8ff6]576 return false;
[e16e0d59]577 case EOVERFLOW:
[cf5ddf6]578 printf("Duplicate symbol %s.\n", symname);
[91c78c9]579 symtab_print_search(symname);
[d6c8ff6]580 return false;
581 case ENOTSUP:
[e16e0d59]582 printf("No symbol information available.\n");
[d6c8ff6]583 return false;
[4ce914d4]584 case EOK:
585 if (isaddr)
[96b02eb9]586 *result = (sysarg_t) symaddr;
[4ce914d4]587 else if (isptr)
[96b02eb9]588 *result = **((sysarg_t **) symaddr);
[4ce914d4]589 else
[96b02eb9]590 *result = *((sysarg_t *) symaddr);
[4ce914d4]591 break;
592 default:
593 printf("Unknown error.\n");
594 return false;
[91c78c9]595 }
[d6c8ff6]596 } else {
597 /* It's a number - convert it */
[4ce914d4]598 uint64_t value;
[9b11a971]599 char *end;
[b7fd2a0]600 errno_t rc = str_uint64_t(text, &end, 0, false, &value);
[9b11a971]601 if (end != text + len)
602 rc = EINVAL;
[4ce914d4]603 switch (rc) {
604 case EINVAL:
[a7d8739]605 printf("Invalid number '%s'.\n", text);
[4ce914d4]606 return false;
607 case EOVERFLOW:
[a7d8739]608 printf("Integer overflow in '%s'.\n", text);
[4ce914d4]609 return false;
610 case EOK:
[96b02eb9]611 *result = (sysarg_t) value;
[4ce914d4]612 if (isptr)
[96b02eb9]613 *result = *((sysarg_t *) *result);
[4ce914d4]614 break;
615 default:
[a7d8739]616 printf("Unknown error parsing '%s'.\n", text);
[4ce914d4]617 return false;
618 }
[c102a5c8]619 }
[a35b458]620
[d6c8ff6]621 return true;
622}
[d0da921]623
[d6c8ff6]624/** Parse argument.
625 *
626 * Find start and end positions of command line argument.
627 *
628 * @param cmdline Command line as read from the input device.
629 * @param size Size (in bytes) of the string.
630 * @param start On entry, 'start' contains pointer to the offset
631 * of the first unprocessed character of cmdline.
632 * On successful exit, it marks beginning of the next argument.
633 * @param end Undefined on entry. On exit, 'end' is the offset of the first
634 * character behind the next argument.
635 *
636 * @return False on failure, true on success.
637 *
638 */
[7a0359b]639NO_TRACE static bool parse_argument(const char *cmdline, size_t size,
640 size_t *start, size_t *end)
[d6c8ff6]641{
[63e27ef]642 assert(start != NULL);
643 assert(end != NULL);
[a35b458]644
[d6c8ff6]645 bool found_start = false;
646 size_t offset = *start;
647 size_t prev = *start;
648 wchar_t ch;
[a35b458]649
[d6c8ff6]650 while ((ch = str_decode(cmdline, &offset, size)) != 0) {
651 if (!found_start) {
652 if (!isspace(ch)) {
653 *start = prev;
654 found_start = true;
655 }
656 } else {
657 if (isspace(ch))
658 break;
659 }
[a35b458]660
[d6c8ff6]661 prev = offset;
662 }
[a7b1071]663 *end = prev;
[a35b458]664
[d6c8ff6]665 return found_start;
[91c78c9]666}
667
[ff3b3197]668/** Parse command line.
669 *
[1e01a35]670 * @param cmdline Command line as read from input device.
[d6c8ff6]671 * @param size Size (in bytes) of the string.
[ff3b3197]672 *
673 * @return Structure describing the command.
[d6c8ff6]674 *
[ff3b3197]675 */
[7a0359b]676NO_TRACE static cmd_info_t *parse_cmdline(const char *cmdline, size_t size)
[ff3b3197]677{
[d6c8ff6]678 size_t start = 0;
679 size_t end = 0;
680 if (!parse_argument(cmdline, size, &start, &end)) {
[ff3b3197]681 /* Command line did not contain alphanumeric word. */
682 return NULL;
683 }
684 spinlock_lock(&cmd_lock);
[a35b458]685
[d6c8ff6]686 cmd_info_t *cmd = NULL;
[a35b458]687
[feeac0d]688 list_foreach(cmd_list, link, cmd_info_t, hlp) {
[ff3b3197]689 spinlock_lock(&hlp->lock);
[a35b458]690
[d6c8ff6]691 if (str_lcmp(hlp->name, cmdline + start,
[08e103d4]692 max(str_code_points(hlp->name),
693 str_ncode_points(cmdline + start, (size_t) (end - start)))) == 0) {
[ff3b3197]694 cmd = hlp;
695 break;
696 }
[a35b458]697
[ff3b3197]698 spinlock_unlock(&hlp->lock);
699 }
[a35b458]700
[d6c8ff6]701 spinlock_unlock(&cmd_lock);
[a35b458]702
[ff3b3197]703 if (!cmd) {
704 /* Unknown command. */
[f4338d2]705 printf("Unknown command.\n");
[ff3b3197]706 return NULL;
707 }
[a35b458]708
[ff3b3197]709 /* cmd == hlp is locked */
[a35b458]710
[ff3b3197]711 /*
712 * The command line must be further analyzed and
713 * the parameters therefrom must be matched and
714 * converted to those specified in the cmd info
715 * structure.
716 */
[a35b458]717
[d6c8ff6]718 bool error = false;
[98000fb]719 size_t i;
[f4338d2]720 for (i = 0; i < cmd->argc; i++) {
[c0f13d2]721 char *buf;
[a35b458]722
[d6c8ff6]723 start = end;
724 if (!parse_argument(cmdline, size, &start, &end)) {
[c0f13d2]725 if (cmd->argv[i].type == ARG_TYPE_STRING_OPTIONAL) {
726 buf = (char *) cmd->argv[i].buffer;
727 str_cpy(buf, cmd->argv[i].len, "");
728 continue;
729 }
[a35b458]730
[f4338d2]731 printf("Too few arguments.\n");
732 spinlock_unlock(&cmd->lock);
733 return NULL;
734 }
[a35b458]735
[f4338d2]736 switch (cmd->argv[i].type) {
[6e716a59]737 case ARG_TYPE_STRING:
[c0f13d2]738 case ARG_TYPE_STRING_OPTIONAL:
[093752c]739 buf = (char *) cmd->argv[i].buffer;
[f4b1535]740 str_ncpy(buf, cmd->argv[i].len, cmdline + start,
[87d71bf]741 end - start);
[f4338d2]742 break;
[d6c8ff6]743 case ARG_TYPE_INT:
744 if (!parse_int_arg(cmdline + start, end - start,
[cf5ddf6]745 &cmd->argv[i].intval))
[d6c8ff6]746 error = true;
[6e716a59]747 break;
[91c78c9]748 case ARG_TYPE_VAR:
[d6c8ff6]749 if ((start < end - 1) && (cmdline[start] == '"')) {
750 if (cmdline[end - 1] == '"') {
751 buf = (char *) cmd->argv[i].buffer;
[f4b1535]752 str_ncpy(buf, cmd->argv[i].len,
753 cmdline + start + 1,
754 (end - start) - 1);
[96b02eb9]755 cmd->argv[i].intval = (sysarg_t) buf;
[d6c8ff6]756 cmd->argv[i].vartype = ARG_TYPE_STRING;
757 } else {
[b62d5614]758 printf("Wrong syntax.\n");
[d6c8ff6]759 error = true;
760 }
761 } else if (parse_int_arg(cmdline + start,
762 end - start, &cmd->argv[i].intval)) {
[91c78c9]763 cmd->argv[i].vartype = ARG_TYPE_INT;
[cf5ddf6]764 } else {
[91c78c9]765 printf("Unrecognized variable argument.\n");
[d6c8ff6]766 error = true;
[6e716a59]767 }
[91c78c9]768 break;
[6e716a59]769 case ARG_TYPE_INVALID:
770 default:
[d6c8ff6]771 printf("Invalid argument type\n");
772 error = true;
[f4338d2]773 break;
774 }
775 }
[a35b458]776
[80bff342]777 if (error) {
778 spinlock_unlock(&cmd->lock);
779 return NULL;
780 }
[a35b458]781
[d6c8ff6]782 start = end;
783 if (parse_argument(cmdline, size, &start, &end)) {
[f4338d2]784 printf("Too many arguments.\n");
785 spinlock_unlock(&cmd->lock);
786 return NULL;
787 }
[a35b458]788
[ff3b3197]789 spinlock_unlock(&cmd->lock);
790 return cmd;
791}
792
[d6c8ff6]793/** Kernel console prompt.
[f4338d2]794 *
[d6c8ff6]795 * @param prompt Kernel console prompt (e.g kconsole/panic).
796 * @param msg Message to display in the beginning.
797 * @param kcon Wait for keypress to show the prompt
798 * and never exit.
[f4338d2]799 *
800 */
[a000878c]801void kconsole(const char *prompt, const char *msg, bool kcon)
[f4338d2]802{
[d6c8ff6]803 if (!stdin) {
804 LOG("No stdin for kernel console");
805 return;
806 }
[a35b458]807
[d6c8ff6]808 if (msg)
809 printf("%s", msg);
[a35b458]810
[d6c8ff6]811 if (kcon)
[44b7783]812 indev_pop_character(stdin);
[d6c8ff6]813 else
814 printf("Type \"exit\" to leave the console.\n");
[a35b458]815
[4f3aa76]816 char *buffer = malloc(STR_BOUNDS(MAX_CMDLINE));
817 char *cmdline = malloc(STR_BOUNDS(MAX_CMDLINE));
818 if (!buffer || !cmdline) {
819 // TODO: fix the function so that it does not need allocations
820 printf("Can't start console, out of memory.\n");
821 free(buffer);
822 free(cmdline);
823 return;
824 }
825
[d6c8ff6]826 while (true) {
[4f3aa76]827 wchar_t *tmp = clever_readline((char *) prompt, stdin, buffer);
[08e103d4]828 size_t len = wstr_code_points(tmp);
[d6c8ff6]829 if (!len)
830 continue;
[a35b458]831
[0f06dbc]832 wstr_to_str(cmdline, STR_BOUNDS(MAX_CMDLINE), tmp);
[a35b458]833
[d6c8ff6]834 if ((!kcon) && (len == 4) && (str_lcmp(cmdline, "exit", 4) == 0))
835 break;
[a35b458]836
[d6c8ff6]837 cmd_info_t *cmd_info = parse_cmdline(cmdline, STR_BOUNDS(MAX_CMDLINE));
838 if (!cmd_info)
839 continue;
[a35b458]840
[d6c8ff6]841 (void) cmd_info->func(cmd_info->argv);
[f4338d2]842 }
[4f3aa76]843 free(buffer);
[a7199c2]844 free(cmdline);
[d6c8ff6]845}
[f4338d2]846
[d6c8ff6]847/** Kernel console managing thread.
848 *
849 */
850void kconsole_thread(void *data)
851{
852 kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);
[f4338d2]853}
[b45c443]854
[06e1e95]855/** @}
[b45c443]856 */
Note: See TracBrowser for help on using the repository browser.