summaryrefslogtreecommitdiff
path: root/src/user/highway
diff options
context:
space:
mode:
Diffstat (limited to 'src/user/highway')
-rw-r--r--src/user/highway/cmds.c46
-rw-r--r--src/user/highway/cmds.h7
-rw-r--r--src/user/highway/highway.c24
-rw-r--r--src/user/highway/line.c100
-rw-r--r--src/user/highway/line.h6
-rw-r--r--src/user/highway/main.c16
-rw-r--r--src/user/highway/vars.c98
-rw-r--r--src/user/highway/vars.h17
8 files changed, 290 insertions, 24 deletions
diff --git a/src/user/highway/cmds.c b/src/user/highway/cmds.c
new file mode 100644
index 0000000..d7a89d1
--- /dev/null
+++ b/src/user/highway/cmds.c
@@ -0,0 +1,46 @@
+#include <knob/file.h>
+#include <knob/user.h>
+#include "line.h"
+#include "vars.h"
+
+void source(const char *path) {
+ struct file *f = open_file(path);
+ if (!f)
+ tell_user_sz("couldn't open file.\n");
+ char buf[128];
+ while (read_line_from_file(f, buf, 127))
+ run_line(buf);
+ close_file(f);
+}
+
+void set(const char *arg) {
+ const char *space = arg;
+ while (*space != ' ')
+ if (*space)
+ ++space;
+ else {
+ struct no_null_sn vname = {
+ .data = arg,
+ .length = space - arg
+ };
+ del_var(vname);
+ return;
+ }
+
+ struct no_null_sn vname = {
+ .data = arg,
+ .length = space - arg
+ };
+
+ const char *vstart = space + 1,
+ *vend = vstart;
+ while (*vend)
+ ++vend;
+
+ struct no_null_sn vval = {
+ .data = vstart,
+ .length = vend - vstart
+ };
+
+ set_var(vname, vval);
+} \ No newline at end of file
diff --git a/src/user/highway/cmds.h b/src/user/highway/cmds.h
new file mode 100644
index 0000000..c404d40
--- /dev/null
+++ b/src/user/highway/cmds.h
@@ -0,0 +1,7 @@
+#ifndef CMDS_H
+#define CMDS_H
+
+void source(const char *path);
+void set(const char *arg);
+
+#endif \ No newline at end of file
diff --git a/src/user/highway/highway.c b/src/user/highway/highway.c
deleted file mode 100644
index 7e78472..0000000
--- a/src/user/highway/highway.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <knob/user.h>
-#include <knob/task.h>
-#include <knob/block.h>
-
-//TODO: load a user environment file containing a PATH-like setting.
-//TODO: have an active disk and/or directory
-
-void main() {
- char path_buf[1024 + 4] = "bin/";
- char *const line_buf = path_buf + 4;
-
- tell_user_sz("Highway, Portland Command Shell, started.\n"
- "Type \"exit\" to quit.\n");
- yield_task();
-
- while (1) {
- tell_user_sz("> ");
- ask_user_line_sz(line_buf, 1023);
- if (blockequ(line_buf, "exit", 5))
- return;
- if (!try_run_command_blocking(path_buf))
- tell_user_sz("An error occured trying to run that command.\n");
- }
-} \ No newline at end of file
diff --git a/src/user/highway/line.c b/src/user/highway/line.c
new file mode 100644
index 0000000..ca38c9b
--- /dev/null
+++ b/src/user/highway/line.c
@@ -0,0 +1,100 @@
+#include <knob/block.h>
+#include <knob/quit.h>
+#include <knob/task.h>
+#include <knob/user.h>
+#include "cmds.h"
+#include "vars.h"
+
+#define LINE_SIZE 4096
+static char line[LINE_SIZE];
+
+static void line_replace(const char *from) {
+ const char *fi = from;
+ char *ti = line;
+ while (*fi) {
+ if (ti == line + LINE_SIZE) {
+ tell_user_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++) {
+ tell_user_sz("Unterminated variable name.\n");
+ line[0] = '\0';
+ return;
+ }
+ if (ti + (var_end - var_start) >= line + LINE_SIZE) {
+ tell_user_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", space - line))
+ source(space + 1);
+ else if (blockequ(line, "set", space - line))
+ set(space + 1);
+ else if (blockequ(line, "echo", space - line)) {
+ tell_user_sz(space + 1);
+ tell_user_sz("\n");
+ }
+ else if (blockequ(line, "vars", space - line))
+ dump_vars();
+ else if (blockequ(line, "quit", space - line))
+ quit(space + 1);
+ else if (blockequ(line, "help", space - line))
+ tell_user_sz("Highway is a command shell for Portland OS. It includes variables and a couple\n"
+ "of pseudo-commands. Variables are addressed by surrounding with \"$\". The\n"
+ "following list shows each of the pseudo-commands.\n\n"
+ " source FILE run each command in FILE\n"
+ " set VAR VALUE set $VAR$ to VALUE\n"
+ " echo STRING print STRING\n"
+ " vars dump variables\n"
+ " quit exit highway\n"
+ " help show this\n\n");
+ else if (!try_run_command_blocking(line)) {
+ struct no_null_sn arg = {
+ .data = "_path",
+ .length = 5
+ };
+ const struct no_null_sn *path = get_var(arg);
+ if (!path->length) {
+ tell_user_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)) {
+ tell_user_sz("Could not run command.\n");
+ return;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/user/highway/line.h b/src/user/highway/line.h
new file mode 100644
index 0000000..4785034
--- /dev/null
+++ b/src/user/highway/line.h
@@ -0,0 +1,6 @@
+#ifndef LINE_H
+#define LINE_H
+
+void run_line(const char *line);
+
+#endif \ No newline at end of file
diff --git a/src/user/highway/main.c b/src/user/highway/main.c
new file mode 100644
index 0000000..1934920
--- /dev/null
+++ b/src/user/highway/main.c
@@ -0,0 +1,16 @@
+#include <knob/user.h>
+#include <knob/task.h>
+#include "cmds.h"
+#include "line.h"
+
+void main(const char *arg) {
+ source(*arg ? arg : "user/default.rc");
+ char cmd_buf[128];
+ yield_task();
+ tell_user_sz("Portland Highway\nType \"help\" for help.\n");
+ while (1) {
+ tell_user_sz("> ");
+ ask_user_line_sz(cmd_buf, 127);
+ run_line(cmd_buf);
+ }
+} \ No newline at end of file
diff --git a/src/user/highway/vars.c b/src/user/highway/vars.c
new file mode 100644
index 0000000..6090b76
--- /dev/null
+++ b/src/user/highway/vars.c
@@ -0,0 +1,98 @@
+#include <knob/block.h>
+#include <knob/heap.h>
+#include <knob/user.h>
+
+struct no_null_sn {
+ char *data;
+ uint32_t length;
+};
+
+struct var_dict_node {
+ struct var_dict_node *next;
+ struct var_dict_node *prev;
+ struct no_null_sn name;
+ struct no_null_sn value;
+};
+
+static struct var_dict_node *var_dict_start = 0;
+
+__attribute__ ((pure))
+static struct var_dict_node *get_node(struct no_null_sn name) {
+ for (struct var_dict_node *i = var_dict_start; i; i = i->next)
+ if ((i->name.length == name.length) &&
+ blockequ(i->name.data, name.data, name.length))
+ return i;
+ return 0;
+}
+
+static struct var_dict_node *new_node() {
+ struct var_dict_node *node = get_block(sizeof(struct var_dict_node));
+ node->prev = 0;
+ node->next = var_dict_start;
+ if (var_dict_start)
+ var_dict_start->prev = node;
+ var_dict_start = node;
+ return node;
+}
+
+void set_var(struct no_null_sn name, struct no_null_sn value) {
+ struct var_dict_node *node = get_node(name);
+ if (node)
+ free_block(node->value.data);
+ else {
+ node = new_node();
+ node->name.length = name.length;
+ node->name.data = get_block(name.length);
+ blockcpy(node->name.data, name.data, name.length);
+ }
+ node->value.length = value.length;
+ node->value.data = get_block(value.length);
+ blockcpy(node->value.data, value.data, value.length);
+}
+
+__attribute__ ((pure))
+const struct no_null_sn *get_var(struct no_null_sn name) {
+ struct var_dict_node *node = get_node(name);
+ return node ? &node->value : 0;
+}
+
+void del_var(struct no_null_sn name) {
+ struct var_dict_node *node = get_node(name);
+ if (!node)
+ return;
+ free_block(node->name.data);
+ free_block(node->value.data);
+ if (node->prev)
+ node->prev->next = node->next;
+ if (node->next)
+ node->next->prev = node->prev;
+ if (node == var_dict_start)
+ var_dict_start = node->next;
+ free_block(node);
+}
+
+void dump_vars() {
+ for (struct var_dict_node *node = var_dict_start; node; node = node->next) {
+ tell_user_sz("$");
+
+ char *buf = get_block(node->name.length + 1);
+ blockcpy(buf, node->name.data, node->name.length);
+ buf[node->name.length] = '\0';
+
+ tell_user_sz(buf);
+
+ free_block(buf);
+
+ tell_user_sz("$ = ");
+
+ buf = get_block(node->value.length + 1);
+ blockcpy(buf, node->value.data, node->value.length);
+ buf[node->value.length] = '\0';
+
+ tell_user_sz(buf);
+
+ free_block(buf);
+
+ tell_user_sz("\n");
+ }
+} \ No newline at end of file
diff --git a/src/user/highway/vars.h b/src/user/highway/vars.h
new file mode 100644
index 0000000..fc4d197
--- /dev/null
+++ b/src/user/highway/vars.h
@@ -0,0 +1,17 @@
+#ifndef VARS_H
+#define VARS_H
+
+#include <stdint.h>
+
+struct no_null_sn {
+ const char *data;
+ uint32_t length;
+};
+
+void set_var(struct no_null_sn name, struct no_null_sn value);
+const struct no_null_sn *get_var(struct no_null_sn name) __attribute__ ((pure));
+void del_var(struct no_null_sn name);
+
+void dump_vars();
+
+#endif \ No newline at end of file