<commit_before>/*******************************************************************************
 *
 * kit/kernel/shell.c
 * - kernel hacking command interface
 *
 * vim:ts=2:sw=2:et:tw=80:ft=c
 *
 * Copyright (C) 2013, <NAME>
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#include "shell.h"
#include "string.h"
#include "terminal.h"
#include "keyboard.h"
#include "memory.h"
#include "paging.h"
#include "interrupt.h"
#include "ps2_8042.h"
#include "debug.h"
#include "test.h"
#include "config.h"

static void shell_display_prompt(uint64_t lineno)
{
  terminal_writechar('\n');
  terminal_setcolor(COLOR_BLACK, COLOR_GREEN);
  terminal_printf("[%lu]", lineno);
  terminal_setcolor(COLOR_WHITE, COLOR_BLACK);
  terminal_writechar(' ');
}

static void shell_read_line(char *buffer, size_t size)
{
  size_t index = 0;

  keyboard_event_t event;

  event.keychar = 0;

  while (index < size - 1)
  {
    keyboard_wait_dequeue(&event);

    if (event.pressed && event.keychar != 0)
    {
      terminal_writechar(event.keychar);
      buffer[index++] = event.keychar;

      if (event.keychar == '\n') break;
    }
  }

  buffer[index] = '\0';
}

static int shell_command_clear(UNUSED int argc, UNUSED char **argv)
{
  terminal_clear();
  return 0;
}

static int shell_command_echo(int argc, char **argv)
{
  for (int i = 1; i < argc; i++)
  {
    if (i > 1) terminal_writechar(' ');
    terminal_writestring(argv[i]);
  }

  terminal_writechar('\n');

  return 0;
}

static int shell_command_reboot(UNUSED int argc, UNUSED char **argv)
{
  ps2_8042_cpu_reset(); // this should not return

  terminal_setcolor(COLOR_RED, COLOR_BLACK);
  terminal_writestring("E: ps2_8042_cpu_reset() failed\n");

  return 1;
}

static int shell_command_mem(UNUSED int argc, UNUSED char **argv)
{
  uint64_t pages = memory_get_total_free();

  paging_pageset_t *pageset = paging_get_current_pageset();

  terminal_printf(
      " free:      %lu pages (%lu MiB)\n"
      " pageset:   %p\n"
      " PML4:      %p (phy %#lx)\n"
      " table_map: %lu entries (root %p)\n",

      pages, pages / 256,
      pageset,
      pageset->pml4, pageset->pml4_physical,
      pageset->table_map.entries, pageset->table_map.tree.root);

  return 0;
}

static int shell_command_test(int argc, char **argv)
{
  if (argc < 2)
  {
    terminal_writestring(
        "Usage: test <unit-name>\n"
        "       test all\n"
        "\n"
        "Units available for testing:\n"
        "\n  ");

    for (size_t i = 0; i < TEST_UNITS_SIZE; i++)
    {
      if (i != 0)
      {
        if (i % 5 == 0)
          terminal_writestring("\n  ");
        else
          terminal_writestring(", ");
      }
      terminal_writestring(test_units[i].name);<commit_msg>[shell] backspace support in shell_read_line()<commit_after>/*******************************************************************************
 *
 * kit/kernel/shell.c
 * - kernel hacking command interface
 *
 * vim:ts=2:sw=2:et:tw=80:ft=c
 *
 * Copyright (C) 2013, <NAME>
 * Redistribution of this file is permitted under the terms of the simplified
 * BSD license. See LICENSE for more information.
 *
 ******************************************************************************/

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#include "shell.h"
#include "string.h"
#include "terminal.h"
#include "keyboard.h"
#include "memory.h"
#include "paging.h"
#include "interrupt.h"
#include "ps2_8042.h"
#include "debug.h"
#include "test.h"
#include "config.h"

static void shell_display_prompt(uint64_t lineno)
{
  terminal_writechar('\n');
  terminal_setcolor(COLOR_BLACK, COLOR_GREEN);
  terminal_printf("[%lu]", lineno);
  terminal_setcolor(COLOR_WHITE, COLOR_BLACK);
  terminal_writechar(' ');
}

static void shell_read_line(char *buffer, size_t size)
{
  size_t index = 0;

  keyboard_event_t event;

  event.keychar = 0;

  while (index < size - 1)
  {
    keyboard_wait_dequeue(&event);

    if (event.pressed && event.keychar != 0)
    {
      if (event.keychar == '\b')
      {
        // Handle backspace only if there are characters to erase.
        if (index > 0)
        {
          terminal_writechar('\b');
          index--;
        }
      }
      else
      {
        terminal_writechar(event.keychar);
        buffer[index++] = event.keychar;

        if (event.keychar == '\n') break;
      }
    }
  }

  buffer[index] = '\0';
}

static int shell_command_clear(UNUSED int argc, UNUSED char **argv)
{
  terminal_clear();
  return 0;
}

static int shell_command_echo(int argc, char **argv)
{
  for (int i = 1; i < argc; i++)
  {
    if (i > 1) terminal_writechar(' ');
    terminal_writestring(argv[i]);
  }

  terminal_writechar('\n');

  return 0;
}

static int shell_command_reboot(UNUSED int argc, UNUSED char **argv)
{
  ps2_8042_cpu_reset(); // this should not return

  terminal_setcolor(COLOR_RED, COLOR_BLACK);
  terminal_writestring("E: ps2_8042_cpu_reset() failed\n");

  return 1;
}

static int shell_command_mem(UNUSED int argc, UNUSED char **argv)
{
  uint64_t pages = memory_get_total_free();

  paging_pageset_t *pageset = paging_get_current_pageset();

  terminal_printf(
      " free:      %lu pages (%lu MiB)\n"
      " pageset:   %p\n"
      " PML4:      %p (phy %#lx)\n"
      " table_map: %lu entries (root %p)\n",

      pages, pages / 256,
      pageset,
      pageset->pml4, pageset->pml4_physical,
      pageset->table_map.entries, pageset->table_map.tree.root);

  return 0;
}

static int shell_command_test(int argc, char **argv)
{
  if (argc < 2)
  {
    terminal_writestring(
        " Usage: test <unit-name>\n"
        "        test all\n"
        "\n"
        " Units available for testing:\n"
        "\n   ");

    for (size_t i = 0; i < TEST_UNITS_SIZE; i++)
    {
      if (i != 0)
      {
        if (i % 5 == 0)
          terminal_writestring("\n   ");
        else
          terminal_writestring(", ");
      }
      terminal_writestring(test_units[i].name);
{'fraction_non_alphanumeric': 0.18451320265673093, 'fraction_numerical': 0.016037583022841407, 'mean_word_length': 3.1744421906693714, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1821208', 'n_tokens_mistral': 2253, 'n_tokens_neox': 2141, 'n_words': 487}