refactor: restructure the user space stuff.
[lunaix-os.git] / lunaix-os / usr / sh / sh.c
diff --git a/lunaix-os/usr/sh/sh.c b/lunaix-os/usr/sh/sh.c
new file mode 100644 (file)
index 0000000..8ce8617
--- /dev/null
@@ -0,0 +1,181 @@
+#include <errno.h>
+#include <lunaix/ioctl.h>
+#include <lunaix/lunaix.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+char pwd[512];
+char cat_buf[1024];
+
+/*
+    Simple shell - (actually this is not even a shell)
+    It just to make the testing more easy.
+*/
+
+#define WS_CHAR(c)                                                             \
+    (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\v' || c == '\r')
+
+void
+strrtrim(char* str)
+{
+    size_t l = strlen(str);
+    while (l < (size_t)-1) {
+        char c = str[l];
+        if (!c || WS_CHAR(c)) {
+            l--;
+            continue;
+        }
+        break;
+    }
+    str[l + 1] = '\0';
+}
+
+char*
+strltrim_safe(char* str)
+{
+    size_t l = 0;
+    char c = 0;
+    while ((c = str[l]) && WS_CHAR(c)) {
+        l++;
+    }
+
+    if (!l)
+        return str;
+    return strcpy(str, str + l);
+}
+
+void
+parse_cmdline(char* line, char** cmd, char** arg_part)
+{
+    strrtrim(line);
+    line = strltrim_safe(line);
+    int l = 0;
+    char c = 0;
+    while ((c = line[l])) {
+        if (c == ' ') {
+            line[l++] = 0;
+            break;
+        }
+        l++;
+    }
+    *cmd = line;
+    if (c) {
+        *arg_part = strltrim_safe(line + l);
+    } else {
+        *arg_part = NULL;
+    }
+}
+
+void
+sh_printerr()
+{
+    switch (errno) {
+        case 0:
+            break;
+        case ENOTDIR:
+            printf("Error: Not a directory\n");
+            break;
+        case ENOENT:
+            printf("Error: No such file or directory\n");
+            break;
+        case EINVAL:
+            printf("Error: Invalid parameter or operation\n");
+            break;
+        case ENOTSUP:
+            printf("Error: Not supported\n");
+            break;
+        case EROFS:
+            printf("Error: File system is read only\n");
+            break;
+        case ENOMEM:
+            printf("Error: Out of memory\n");
+            break;
+        case EISDIR:
+            printf("Error: This is a directory\n");
+            break;
+        default:
+            printf("Error: (%d)\n", errno);
+            break;
+    }
+}
+
+void
+sigint_handle(int signum)
+{
+    return;
+}
+
+void
+sh_exec(const char* name, const char** argv)
+{
+    if (!strcmp(name, "cd")) {
+        chdir(argv[0]);
+        sh_printerr();
+        return;
+    }
+
+    pid_t p;
+    if (!(p = fork())) {
+        if (execve(name, argv, NULL)) {
+            sh_printerr();
+        }
+        _exit(1);
+    }
+    setpgid(p, getpgid());
+    waitpid(p, NULL, 0);
+}
+
+void
+sh_loop()
+{
+    char buf[512];
+    char *cmd, *argpart;
+    signal(SIGINT, sigint_handle);
+
+    // set our shell as foreground process
+    // (unistd.h:tcsetpgrp is essentially a wrapper of this)
+    // stdout (by default, unless user did smth) is the tty we are currently at
+    ioctl(stdout, TIOCSPGRP, getpgid());
+
+    char* argv[] = { 0, 0 };
+
+    while (1) {
+        getcwd(pwd, 512);
+        printf("[\033[2m%s\033[39;49m]$ ", pwd);
+        int sz = read(stdin, buf, 511);
+
+        if (sz < 0) {
+            printf("fail to read user input (%d)\n", geterrno());
+            return;
+        }
+
+        buf[sz] = '\0';
+
+        // currently, this shell only support single argument
+        parse_cmdline(buf, &cmd, &argv[0]);
+
+        if (cmd[0] == 0) {
+            printf("\n");
+            goto cont;
+        }
+
+        // cmd=="exit"
+        if (*(unsigned int*)cmd == 0x74697865U) {
+            break;
+        }
+
+        sh_exec(cmd, (const char**)&argv);
+    cont:
+        printf("\n");
+    }
+}
+
+void
+main()
+{
+    printf("\n Simple shell. Use <PG_UP> or <PG_DOWN> to scroll.\n\n");
+    sh_loop();
+    _exit(0);
+}
\ No newline at end of file