summaryrefslogtreecommitdiff
path: root/src/user/highway/line.c
blob: bf171e56772bd96d5914778ec2449932ecdcc668 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <terminal/terminal.h>

#include <knob/block.h>
#include <knob/task.h>

#include <pland/pcrt.h>

#include "cmds.h"
#include "term.h"
#include "vars.h"

#define LINE_SIZE 4096
static char line[LINE_SIZE];

static inline uint8_t hex_to_int(char ch) {
  return ch - (ch <= '9' ? '0' : 'a' - 10);
}

void ensure_color() {
  const struct no_null_sn *fg = get_var((struct no_null_sn){.data = "_color_fg", .length = 9});
  const struct no_null_sn *bg = get_var((struct no_null_sn){.data = "_color_bg", .length = 9});
  if (fg && bg)
    set_color(
      (hex_to_int(fg->data[0]) << 4) | hex_to_int(fg->data[1]),
      (hex_to_int(bg->data[0]) << 4) | hex_to_int(bg->data[1])
    );
}

static void line_replace(const char *from) {
  const char *fi = from;
  char *ti = line;
  while (*fi) {
    if (ti == line + LINE_SIZE) {
      term_add_sz("Line too long.\n");
      line[0] = '\0';
      return;
    }
    if (*fi != '$') {
      *ti = *fi;
      ++ti;
      ++fi;
      continue;
    }
    const char *var_start = ++fi;
    const char *var_end = var_start;
    while (*var_end != '$')
      if (!*var_end++) {
        if (var_end - fi > 10)
          term_addf("Unterminated variable at\"%10s...\".\n", fi);
        else
          term_addf("Unterminated variable at \"%s\".\n", fi);
        line[0] = '\0';
        return;
      }
    if (ti + (var_end - var_start) >= line + LINE_SIZE) {
      term_add_sz("Line too long.\n");
      line[0] = '\0';
      return;
    }
    struct no_null_sn vname = {
      .data = var_start,
      .length = var_end - var_start
    };
    const struct no_null_sn *vval = get_var(vname);
    if (!vval)
      vval = &vname;
    blockcpy(ti, vval->data, vval->length);
    ti += vval->length;
    fi = var_end + 1;
  }
  *ti = '\0';
}

void run_line(const char *original_line) {
  line_replace(original_line);
  if (!*line)
    return;
  const char *space;
  for (space = line; *space && (*space != ' '); ++space)
    ;
  if (blockequ(line, "source ", 7))
    source(space + 1);
  else if (blockequ(line, "set ", 4)) {
    set(space + 1);
    ensure_color();
  }
  else if (blockequ(line, "echo ", 5)) {
    term_add_sz(space + 1);
    term_add_char('\n');
  }
  else if (blockequ(line, "vars", 5))
    dump_vars();
  else if (blockequ(line, "quit", 5)) {
    del_term(active_term);
    __pcrt_quit();
  }
  else if (blockequ(line, "clear", 6))
    clear_term();
  else if (blockequ(line, "help", 5))
    term_add_sz("Highway is a command shell for Portland OS. It includes variable support and a couple of pseudo-commands. Variables are addressed by surrounding with \"$\". The following list shows each of the pseudo-commands.\n\n"
                "  source FILE\t"   "run each command in FILE\n"
                "  clear\t\t\t"     "clear the screen\n"
                "  echo STRING\t"   "print STRING\n"
                "  set VAR VALUE\t" "set $VAR$ to VALUE\n"
                "  vars\t\t\t"      "dump variables\n"
                "  quit\t\t\t"      "exit highway\n"
                "  help\t\t\t"      "show this\n");
  else if (!try_run_command_blocking(line, stdio_task)) {
    const struct no_null_sn *path = get_var((struct no_null_sn){.data = "_path", .length = 5});
    if (!path->length) {
      term_add_sz("Could not run command.\n");
      return;
    }
    for (uint16_t to_i = LINE_SIZE - 1; to_i >= path->length; --to_i)
      line[to_i] = line[to_i - path->length];
    blockcpy(line, path->data, path->length);
    if (!try_run_command_blocking(line, stdio_task)) {
      term_add_sz("Could not run command.\n");
      return;
    }
    else {
      ensure_color();
      if (active_term->cursor_x)
        term_newline();
    }
  }
  else
    ensure_color();
}