Forráskód Böngészése

Merge branch 'shell'

greatbridf 2 éve
szülő
commit
b22ee32e67
1 módosított fájl, 545 hozzáadás és 95 törlés
  1. 545 95
      user-space-program/sh.c

+ 545 - 95
user-space-program/sh.c

@@ -1,106 +1,556 @@
+#include <stdint.h>
+#include <stdarg.h>
 #include <sys/wait.h>
 #include <stdio.h>
 #include <string.h>
+#include <fcntl.h>
 #include <unistd.h>
 
-#define print(str) write(STDOUT_FILENO, str, strlen(str))
-
-// struct cmd {
-//     enum {
-//         pipe,
-//         list,
-//     } op;
-//     struct cmd* left;
-//     struct cmd* right;
-//     struct token* tk;
-// };
-
-// struct token {
-//     char* start;
-//     size_t len;
-
-//     struct token* next;
-// };
-
-// static inline int betwn(char c, char s, char e)
-// {
-//     return c >= s && c <= e;
-// }
-// static inline int is_num(char c)
-// {
-//     return betwn(c, '0', '9');
-// }
-// static inline int check_token(char c)
-// {
-//     return betwn(c, 'a', 'z') || betwn(c, 'A', 'Z') || (c == '_');
-// }
-// static inline int read_token(char* buf, struct token** cur)
-// {
-//     char* p = buf;
-//     for (; *p && (check_token(*p) || (p != buf && is_num(*p))); ++p) {
-//         (*cur)->start = buf;
-//         ++(*cur)->len;
+int printf(const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+
+    char buf[256] = {};
+    int len = snprintf(buf, sizeof(buf), fmt, args);
+
+    len = write(STDOUT_FILENO, buf, len);
+
+    va_end(args);
+
+    return len;
+}
+
+char* strchr(const char* str, int c)
+{
+    const char* p = str;
+    while (*p) {
+        if (*p == c)
+            return (char*)p;
+        ++p;
+    }
+    return NULL;
+}
+
+char* gets(char* buf, int bufsize)
+{
+    int n = read(STDIN_FILENO, buf, bufsize);
+    if (n > 0 && buf[n-1] == '\n') {
+        buf[n-1] = 0;
+        return buf;
+    }
+    return NULL;
+}
+
+int puts(const char* str)
+{
+    int len = strlen(str);
+    write(STDOUT_FILENO, str, len);
+    return len + 1;
+}
+
+void* malloc(size_t n)
+{
+    static char mems[1024];
+    static int pos = 0;
+    if (n == 0) {
+        pos = 0;
+        return 0;
+    }
+    int orig_pos = pos;
+    pos += n;
+    return mems + orig_pos;
+}
+
+// Parsed command representation
+#define EXEC  1
+#define REDIR 2
+#define PIPE  3
+#define LIST  4
+#define BACK  5
+
+#define MAXARGS 10
+
+struct cmd {
+  int type;
+};
+
+struct execcmd {
+  int type;
+  char *argv[MAXARGS];
+  char *eargv[MAXARGS];
+};
+
+struct redircmd {
+  int type;
+  struct cmd *cmd;
+  char *file;
+  char *efile;
+  int mode;
+  int fd;
+};
+
+struct pipecmd {
+  int type;
+  struct cmd *left;
+  struct cmd *right;
+};
+
+struct listcmd {
+  int type;
+  struct cmd *left;
+  struct cmd *right;
+};
+
+struct backcmd {
+  int type;
+  struct cmd *cmd;
+};
+
+int fork1(void);  // Fork but panics on failure.
+void panic(char*);
+struct cmd *parsecmd(char*);
+
+// Execute cmd.  Never returns.
+void
+runcmd(struct cmd *cmd)
+{
+//   int p[2];
+  struct backcmd *bcmd;
+  struct execcmd *ecmd;
+  struct listcmd *lcmd;
+//   struct pipecmd *pcmd;
+//   struct redircmd *rcmd;
+
+  if(cmd == 0)
+    _exit(-1);
+  
+  switch(cmd->type){
+  default:
+    panic("runcmd");
+
+  case EXEC:
+    ecmd = (struct execcmd*)cmd;
+    if(ecmd->argv[0] == 0)
+      _exit(-1);
+    char* const envp[1] = { NULL };
+    execve(ecmd->argv[0], ecmd->argv, envp);
+    printf("exec %s failed\n", ecmd->argv[0]);
+    break;
+
+//   case REDIR:
+//     rcmd = (struct redircmd*)cmd;
+//     close(rcmd->fd);
+//     if(open(rcmd->file, rcmd->mode) < 0){
+//       printf("open %s failed\n", rcmd->file);
+//       _exit(-1);
 //     }
-//     if ((*cur)->start) {
-//         (*cur)->next = *cur + 1;
-//         *cur = (*cur)->next;
+//     runcmd(rcmd->cmd);
+//     break;
+
+  case LIST:
+    lcmd = (struct listcmd*)cmd;
+    if(fork1() == 0)
+      runcmd(lcmd->left);
+    int code;
+    wait(&code);
+    runcmd(lcmd->right);
+    break;
+
+//   case PIPE:
+//     pcmd = (struct pipecmd*)cmd;
+//     if(pipe(p) < 0)
+//       panic("pipe");
+//     if(fork1() == 0){
+//       close(1);
+//       dup(p[1]);
+//       close(p[0]);
+//       close(p[1]);
+//       runcmd(pcmd->left);
+//     }
+//     if(fork1() == 0){
+//       close(0);
+//       dup(p[0]);
+//       close(p[0]);
+//       close(p[1]);
+//       runcmd(pcmd->right);
+//     }
+//     close(p[0]);
+//     close(p[1]);
+//     wait();
+//     wait();
+//     break;
+    
+  case BACK:
+    bcmd = (struct backcmd*)cmd;
+    if(fork1() == 0)
+      runcmd(bcmd->cmd);
+    break;
+  }
+  _exit(0);
+}
+
+int
+getcmd(char *buf, int nbuf)
+{
+  printf("$ ");
+  memset(buf, 0, nbuf);
+  gets(buf, nbuf);
+  if(buf[0] == 0) // EOF
+    return -1;
+  return 0;
+}
+
+int
+main(void)
+{
+  void* _ = malloc(0);
+  (void)_;
+  static char buf[100];
+  
+//   // Assumes three file descriptors open.
+//   while((fd = open("console", 0)) >= 0){
+//     if(fd >= 3){
+//       close(fd);
+//       break;
 //     }
-//     return p - buf;
-// }
-
-int main(int argc, const char** argv)
-{
-    (void)argc, (void)argv;
-    char buf[512] = {};
-
-    print("sh # ");
-
-    for (;;) {
-        int n = read(STDIN_FILENO, buf, sizeof(buf));
-
-        char token[1024] = {};
-        char* args[128] = { token, 0 };
-        int j = 0;
-        int k = 1;
-
-        for (int i = 0; i < n; ++i) {
-            if (buf[i] == ' ') {
-                token[j++] = 0;
-                args[k++] = token + j;
-                continue;
-            }
-            if (buf[i] == '\n') {
-                token[j++] = 0;
-
-                if (strcmp(args[0], "exit") == 0)
-                    return 0;
-
-                pid_t pid = fork();
-                if (pid == 0) {
-                    char* envp[] = { NULL };
-                    int ret = execve(args[0], args, envp);
-                    char _b[128];
-                    snprintf(_b, sizeof(_b), "sh: execve() failed with code %d\n", ret);
-                    print(_b);
-                    return -1;
-                }
-
-                int code;
-                wait(&code);
-
-                char _b[128];
-                snprintf(_b, sizeof(_b), "sh (%d) # ", code);
-                print(_b);
-
-                j = 0;
-                k = 1;
-                memset(args, 0x00, sizeof(args));
-                args[0] = token;
-                continue;
-            }
-            token[j++] = buf[i];
-        }
+//   }
+  
+  // Read and run input commands.
+  while(getcmd(buf, sizeof(buf)) >= 0){
+    // if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
+    //   // Clumsy but will have to do for now.
+    //   // Chdir has no effect on the parent if run in the child.
+    //   buf[strlen(buf)-1] = 0;  // chop \n
+    //   if(chdir(buf+3) < 0)
+    //     printf("cannot cd %s\n", buf+3);
+    //   continue;
+    // }
+    if(fork1() == 0)
+      runcmd(parsecmd(buf));
+    int code;
+    wait(&code);
+  }
+  _exit(0);
+}
+
+void __attribute__((noreturn))
+panic(char *s)
+{
+  printf("%s\n", s);
+  _exit(-1);
+}
+
+int
+fork1(void)
+{
+  int pid;
+  
+  pid = fork();
+  if(pid == -1)
+    panic("fork");
+  return pid;
+}
+
+//PAGEBREAK!
+// Constructors
+
+struct cmd*
+execcmd(void)
+{
+  struct execcmd *cmd;
+
+  cmd = malloc(sizeof(*cmd));
+  memset(cmd, 0, sizeof(*cmd));
+  cmd->type = EXEC;
+  return (struct cmd*)cmd;
+}
+
+struct cmd*
+redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
+{
+  struct redircmd *cmd;
+
+  cmd = malloc(sizeof(*cmd));
+  memset(cmd, 0, sizeof(*cmd));
+  cmd->type = REDIR;
+  cmd->cmd = subcmd;
+  cmd->file = file;
+  cmd->efile = efile;
+  cmd->mode = mode;
+  cmd->fd = fd;
+  return (struct cmd*)cmd;
+}
+
+struct cmd*
+pipecmd(struct cmd *left, struct cmd *right)
+{
+  struct pipecmd *cmd;
+
+  cmd = malloc(sizeof(*cmd));
+  memset(cmd, 0, sizeof(*cmd));
+  cmd->type = PIPE;
+  cmd->left = left;
+  cmd->right = right;
+  return (struct cmd*)cmd;
+}
+
+struct cmd*
+listcmd(struct cmd *left, struct cmd *right)
+{
+  struct listcmd *cmd;
+
+  cmd = malloc(sizeof(*cmd));
+  memset(cmd, 0, sizeof(*cmd));
+  cmd->type = LIST;
+  cmd->left = left;
+  cmd->right = right;
+  return (struct cmd*)cmd;
+}
+
+struct cmd*
+backcmd(struct cmd *subcmd)
+{
+  struct backcmd *cmd;
+
+  cmd = malloc(sizeof(*cmd));
+  memset(cmd, 0, sizeof(*cmd));
+  cmd->type = BACK;
+  cmd->cmd = subcmd;
+  return (struct cmd*)cmd;
+}
+//PAGEBREAK!
+// Parsing
+
+char whitespace[] = " \t\r\n\v";
+char symbols[] = "<|>&;()";
+
+int
+gettoken(char **ps, char *es, char **q, char **eq)
+{
+  char *s;
+  int ret;
+  
+  s = *ps;
+  while(s < es && strchr(whitespace, *s))
+    s++;
+  if(q)
+    *q = s;
+  ret = *s;
+  switch(*s){
+  case 0:
+    break;
+  case '|':
+  case '(':
+  case ')':
+  case ';':
+  case '&':
+  case '<':
+    s++;
+    break;
+  case '>':
+    s++;
+    if(*s == '>'){
+      ret = '+';
+      s++;
+    }
+    break;
+  default:
+    ret = 'a';
+    while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
+      s++;
+    break;
+  }
+  if(eq)
+    *eq = s;
+  
+  while(s < es && strchr(whitespace, *s))
+    s++;
+  *ps = s;
+  return ret;
+}
+
+int
+peek(char **ps, char *es, char *toks)
+{
+  char *s;
+  
+  s = *ps;
+  while(s < es && strchr(whitespace, *s))
+    s++;
+  *ps = s;
+  return *s && strchr(toks, *s);
+}
+
+struct cmd *parseline(char**, char*);
+struct cmd *parsepipe(char**, char*);
+struct cmd *parseexec(char**, char*);
+struct cmd *nulterminate(struct cmd*);
+
+struct cmd*
+parsecmd(char *s)
+{
+  char *es;
+  struct cmd *cmd;
+
+  es = s + strlen(s);
+  cmd = parseline(&s, es);
+  peek(&s, es, "");
+  if(s != es){
+    printf("leftovers: %s\n", s);
+    panic("syntax");
+  }
+  nulterminate(cmd);
+  return cmd;
+}
+
+struct cmd*
+parseline(char **ps, char *es)
+{
+  struct cmd *cmd;
+
+  cmd = parsepipe(ps, es);
+  while(peek(ps, es, "&")){
+    gettoken(ps, es, 0, 0);
+    cmd = backcmd(cmd);
+  }
+  if(peek(ps, es, ";")){
+    gettoken(ps, es, 0, 0);
+    cmd = listcmd(cmd, parseline(ps, es));
+  }
+  return cmd;
+}
+
+struct cmd*
+parsepipe(char **ps, char *es)
+{
+  struct cmd *cmd;
+
+  cmd = parseexec(ps, es);
+  if(peek(ps, es, "|")){
+    gettoken(ps, es, 0, 0);
+    cmd = pipecmd(cmd, parsepipe(ps, es));
+  }
+  return cmd;
+}
+
+struct cmd*
+parseredirs(struct cmd *cmd, char **ps, char *es)
+{
+  int tok;
+  char *q, *eq;
+
+  while(peek(ps, es, "<>")){
+    tok = gettoken(ps, es, 0, 0);
+    if(gettoken(ps, es, &q, &eq) != 'a')
+      panic("missing file for redirection");
+    switch(tok){
+    case '<':
+      cmd = redircmd(cmd, q, eq, 0, 0);
+      break;
+    case '>':
+      cmd = redircmd(cmd, q, eq, 0, 1);
+      break;
+    case '+':  // >>
+      cmd = redircmd(cmd, q, eq, 0, 1);
+      break;
     }
+  }
+  return cmd;
+}
 
+struct cmd*
+parseblock(char **ps, char *es)
+{
+  struct cmd *cmd;
+
+  if(!peek(ps, es, "("))
+    panic("parseblock");
+  gettoken(ps, es, 0, 0);
+  cmd = parseline(ps, es);
+  if(!peek(ps, es, ")"))
+    panic("syntax - missing )");
+  gettoken(ps, es, 0, 0);
+  cmd = parseredirs(cmd, ps, es);
+  return cmd;
+}
+
+struct cmd*
+parseexec(char **ps, char *es)
+{
+  char *q, *eq;
+  int tok, argc;
+  struct execcmd *cmd;
+  struct cmd *ret;
+  
+  if(peek(ps, es, "("))
+    return parseblock(ps, es);
+
+  ret = execcmd();
+  cmd = (struct execcmd*)ret;
+
+  argc = 0;
+  ret = parseredirs(ret, ps, es);
+  while(!peek(ps, es, "|)&;")){
+    if((tok=gettoken(ps, es, &q, &eq)) == 0)
+      break;
+    if(tok != 'a')
+      panic("syntax");
+    cmd->argv[argc] = q;
+    cmd->eargv[argc] = eq;
+    argc++;
+    if(argc >= MAXARGS)
+      panic("too many args");
+    ret = parseredirs(ret, ps, es);
+  }
+  cmd->argv[argc] = 0;
+  cmd->eargv[argc] = 0;
+  return ret;
+}
+
+// NUL-terminate all the counted strings.
+struct cmd*
+nulterminate(struct cmd *cmd)
+{
+  int i;
+  struct backcmd *bcmd;
+  struct execcmd *ecmd;
+  struct listcmd *lcmd;
+  struct pipecmd *pcmd;
+  struct redircmd *rcmd;
+
+  if(cmd == 0)
     return 0;
+  
+  switch(cmd->type){
+  case EXEC:
+    ecmd = (struct execcmd*)cmd;
+    for(i=0; ecmd->argv[i]; i++)
+      *ecmd->eargv[i] = 0;
+    break;
+
+  case REDIR:
+    rcmd = (struct redircmd*)cmd;
+    nulterminate(rcmd->cmd);
+    *rcmd->efile = 0;
+    break;
+
+  case PIPE:
+    pcmd = (struct pipecmd*)cmd;
+    nulterminate(pcmd->left);
+    nulterminate(pcmd->right);
+    break;
+    
+  case LIST:
+    lcmd = (struct listcmd*)cmd;
+    nulterminate(lcmd->left);
+    nulterminate(lcmd->right);
+    break;
+
+  case BACK:
+    bcmd = (struct backcmd*)cmd;
+    nulterminate(bcmd->cmd);
+    break;
+  }
+  return cmd;
 }