浏览代码

Shell: Move line editing to a separate class.

To be clear, there isn't really any line editing yet. But there is
going to be, so let's have it in its own class.
Andreas Kling 6 年之前
父节点
当前提交
ba7364b43b
共有 6 个文件被更改,包括 167 次插入100 次删除
  1. 2 0
      Kernel/makeall.sh
  2. 18 0
      Shell/GlobalState.h
  3. 94 0
      Shell/LineEditor.cpp
  4. 23 0
      Shell/LineEditor.h
  5. 1 0
      Shell/Makefile
  6. 29 100
      Shell/main.cpp

+ 2 - 0
Kernel/makeall.sh

@@ -46,6 +46,8 @@ $make_cmd -C ../Games/Minesweeper clean && \
 $make_cmd -C ../Games/Minesweeper && \
 $make_cmd -C ../Games/Snake clean && \
 $make_cmd -C ../Games/Snake && \
+$make_cmd -C ../Shell clean && \
+$make_cmd -C ../Shell && \
 $make_cmd clean &&\
 $make_cmd && \
 sudo ./sync.sh

+ 18 - 0
Shell/GlobalState.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <AK/AKString.h>
+#include <termios.h>
+
+struct GlobalState {
+    String cwd;
+    String username;
+    String home;
+    char ttyname[32];
+    char hostname[32];
+    pid_t sid;
+    uid_t uid;
+    struct termios termios;
+    bool was_interrupted { false };
+};
+
+extern GlobalState g;

+ 94 - 0
Shell/LineEditor.cpp

@@ -0,0 +1,94 @@
+#include "LineEditor.h"
+#include "GlobalState.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+
+LineEditor::LineEditor()
+{
+}
+
+LineEditor::~LineEditor()
+{
+}
+
+void LineEditor::add_to_history(const String& line)
+{
+    if ((m_history.size() + 1) > m_history_capacity)
+        m_history.take_first();
+    m_history.append(line);
+}
+
+String LineEditor::get_line()
+{
+    for (;;) {
+        char keybuf[16];
+        ssize_t nread = read(0, keybuf, sizeof(keybuf));
+        // FIXME: exit()ing here is a bit off. Should communicate failure to caller somehow instead.
+        if (nread == 0)
+            exit(0);
+        if (nread < 0) {
+            if (errno == EINTR) {
+                if (g.was_interrupted) {
+                    if (!m_buffer.is_empty())
+                        printf("^C");
+                }
+                g.was_interrupted = false;
+                m_buffer.clear();
+                putchar('\n');
+                return String::empty();
+            } else {
+                perror("read failed");
+                // FIXME: exit()ing here is a bit off. Should communicate failure to caller somehow instead.
+                exit(2);
+            }
+        }
+
+        for (ssize_t i = 0; i < nread; ++i) {
+            char ch = keybuf[i];
+            if (ch == 0)
+                continue;
+            if (ch == 8 || ch == g.termios.c_cc[VERASE]) {
+                if (m_buffer.is_empty())
+                    continue;
+                m_buffer.take_last();
+                putchar(8);
+                fflush(stdout);
+                continue;
+            }
+            if (ch == g.termios.c_cc[VWERASE]) {
+                bool has_seen_nonspace = false;
+                while (!m_buffer.is_empty()) {
+                    if (isspace(m_buffer.last())) {
+                        if (has_seen_nonspace)
+                            break;
+                    } else {
+                        has_seen_nonspace = true;
+                    }
+                    putchar(0x8);
+                    m_buffer.take_last();
+                }
+                fflush(stdout);
+                continue;
+            }
+            if (ch == g.termios.c_cc[VKILL]) {
+                if (m_buffer.is_empty())
+                    continue;
+                for (int i = 0; i < m_buffer.size(); ++i)
+                    putchar(0x8);
+                m_buffer.clear();
+                fflush(stdout);
+                continue;
+            }
+            putchar(ch);
+            fflush(stdout);
+            if (ch != '\n') {
+                m_buffer.append(ch);
+            } else {
+                auto string = String::copy(m_buffer);
+                m_buffer.clear();
+                return string;
+            }
+        }
+    }
+}

+ 23 - 0
Shell/LineEditor.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include <AK/AKString.h>
+#include <AK/Vector.h>
+
+class LineEditor {
+public:
+    LineEditor();
+    ~LineEditor();
+
+    String get_line();
+
+    void add_to_history(const String&);
+    const Vector<String>& history() const { return m_history; }
+
+private:
+    Vector<char, 1024> m_buffer;
+    int m_cursor { 0 };
+
+    // FIXME: This should be something more take_first()-friendly.
+    Vector<String> m_history;
+    int m_history_capacity { 100 };
+};

+ 1 - 0
Shell/Makefile

@@ -2,6 +2,7 @@ include ../Makefile.common
 
 OBJS = \
     Parser.o \
+    LineEditor.o \
     main.o
 
 APP = Shell

+ 29 - 100
Shell/main.cpp

@@ -7,43 +7,33 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <termios.h>
-#include <ctype.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
 #include <AK/FileSystemPath.h>
 #include <LibCore/CElapsedTimer.h>
+#include "GlobalState.h"
 #include "Parser.h"
+#include "LineEditor.h"
 
 //#define SH_DEBUG
 
-struct GlobalState {
-    String cwd;
-    String username;
-    String home;
-    char ttyname[32];
-    char hostname[32];
-    pid_t sid;
-    uid_t uid;
-    struct termios termios;
-    bool was_interrupted { false };
-};
-static GlobalState* g;
+GlobalState g;
 
 static void prompt()
 {
-    if (g->uid == 0)
+    if (g.uid == 0)
         printf("# ");
     else {
-        printf("\033]0;%s@%s:%s\007", g->username.characters(), g->hostname, g->cwd.characters());
-        printf("\033[31;1m%s\033[0m@\033[37;1m%s\033[0m:\033[32;1m%s\033[0m$> ", g->username.characters(), g->hostname, g->cwd.characters());
+        printf("\033]0;%s@%s:%s\007", g.username.characters(), g.hostname, g.cwd.characters());
+        printf("\033[31;1m%s\033[0m@\033[37;1m%s\033[0m:\033[32;1m%s\033[0m$> ", g.username.characters(), g.hostname, g.cwd.characters());
     }
     fflush(stdout);
 }
 
 static int sh_pwd(int, char**)
 {
-    printf("%s\n", g->cwd.characters());
+    printf("%s\n", g.cwd.characters());
     return 0;
 }
 
@@ -57,7 +47,7 @@ void did_receive_signal(int signum)
 
 void handle_sigint(int)
 {
-    g->was_interrupted = true;
+    g.was_interrupted = true;
 }
 
 static int sh_exit(int, char**)
@@ -88,12 +78,12 @@ static int sh_cd(int argc, char** argv)
     char pathbuf[PATH_MAX];
 
     if (argc == 1) {
-        strcpy(pathbuf, g->home.characters());
+        strcpy(pathbuf, g.home.characters());
     } else {
         if (argv[1][0] == '/')
             memcpy(pathbuf, argv[1], strlen(argv[1]) + 1);
         else
-            sprintf(pathbuf, "%s/%s", g->cwd.characters(), argv[1]);
+            sprintf(pathbuf, "%s/%s", g.cwd.characters(), argv[1]);
     }
 
     FileSystemPath canonical_path(pathbuf);
@@ -118,7 +108,7 @@ static int sh_cd(int argc, char** argv)
         printf("chdir(%s) failed: %s\n", path, strerror(errno));
         return 1;
     }
-    g->cwd = canonical_path.string();
+    g.cwd = canonical_path.string();
     return 0;
 }
 
@@ -277,7 +267,7 @@ static int run_command(const String& cmd)
             tcsetpgrp(0, getpid());
             for (auto& redirection : subcommand.redirections) {
                 if (redirection.type == Redirection::Rewire) {
-#ifdef SH_DEBUG
+#ifdef SH_DEBUGsh
                     dbgprintf("in %s<%d>, dup2(%d, %d)\n", argv[0], getpid(), redirection.rewire_fd, redirection.fd);
 #endif
                     int rc = dup2(redirection.rewire_fd, redirection.fd);
@@ -355,11 +345,10 @@ static int run_command(const String& cmd)
 
 int main(int argc, char** argv)
 {
-    g = new GlobalState;
-    g->uid = getuid();
-    g->sid = setsid();
+    g.uid = getuid();
+    g.sid = setsid();
     tcsetpgrp(0, getpgrp());
-    tcgetattr(0, &g->termios);
+    tcgetattr(0, &g.termios);
 
     {
         struct sigaction sa;
@@ -370,18 +359,18 @@ int main(int argc, char** argv)
         assert(rc == 0);
     }
 
-    int rc = gethostname(g->hostname, sizeof(g->hostname));
+    int rc = gethostname(g.hostname, sizeof(g.hostname));
     if (rc < 0)
         perror("gethostname");
-    rc = ttyname_r(0, g->ttyname, sizeof(g->ttyname));
+    rc = ttyname_r(0, g.ttyname, sizeof(g.ttyname));
     if (rc < 0)
         perror("ttyname_r");
 
     {
         auto* pw = getpwuid(getuid());
         if (pw) {
-            g->username = pw->pw_name;
-            g->home = pw->pw_dir;
+            g.username = pw->pw_name;
+            g.home = pw->pw_dir;
             putenv(const_cast<char*>(String::format("HOME=%s", pw->pw_dir).characters()));
         }
         endpwent();
@@ -392,81 +381,21 @@ int main(int argc, char** argv)
         return 1;
     }
 
-    Vector<char, 256> line_buffer;
-
     {
         auto* cwd = getcwd(nullptr, 0);
-        g->cwd = cwd;
+        g.cwd = cwd;
         free(cwd);
     }
-    prompt();
+
+    LineEditor editor;
     for (;;) {
-        char keybuf[16];
-        ssize_t nread = read(0, keybuf, sizeof(keybuf));
-        if (nread == 0)
-            return 0;
-        if (nread < 0) {
-            if (errno == EINTR) {
-                if (g->was_interrupted) {
-                    if (!line_buffer.is_empty())
-                        printf("^C");
-                }
-                g->was_interrupted = false;
-                line_buffer.clear();
-                putchar('\n');
-                prompt();
-                continue;
-            } else {
-                perror("read failed");
-                return 2;
-            }
-        }
-        for (ssize_t i = 0; i < nread; ++i) {
-            char ch = keybuf[i];
-            if (ch == 0)
-                continue;
-            if (ch == 8 || ch == g->termios.c_cc[VERASE]) {
-                if (line_buffer.is_empty())
-                    continue;
-                line_buffer.take_last();
-                putchar(8);
-                fflush(stdout);
-                continue;
-            }
-            if (ch == g->termios.c_cc[VWERASE]) {
-                bool has_seen_nonspace = false;
-                while (!line_buffer.is_empty()) {
-                    if (isspace(line_buffer.last())) {
-                        if (has_seen_nonspace)
-                            break;
-                    } else {
-                        has_seen_nonspace = true;
-                    }
-                    putchar(0x8);
-                    line_buffer.take_last();
-                }
-                fflush(stdout);
-                continue;
-            }
-            if (ch == g->termios.c_cc[VKILL]) {
-                if (line_buffer.is_empty())
-                    continue;
-                for (int i = 0; i < line_buffer.size(); ++i)
-                    putchar(0x8);
-                line_buffer.clear();
-                fflush(stdout);
-                continue;
-            }
-            putchar(ch);
-            fflush(stdout);
-            if (ch != '\n') {
-                line_buffer.append(ch);
-            } else {
-                run_command(String::copy(line_buffer));
-                line_buffer.clear();
-                prompt();
-            }
-        }
+        prompt();
+        auto line = editor.get_line();
+        if (line.is_empty())
+            continue;
+        run_command(line);
+        editor.add_to_history(line);
     }
+
     return 0;
 }