diff --git a/hw/hwsh.c b/hw/hwsh.c new file mode 100644 index 0000000..6a46a08 --- /dev/null +++ b/hw/hwsh.c @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Simplifed xv6 shell. + +#define MAXARGS 10 + +// All commands have at least a type. Have looked at the type, the code +// typically casts the *cmd to some specific cmd type. +struct cmd { + int type; // ' ' (exec), | (pipe), '<' or '>' for redirection +}; + +struct execcmd { + int type; // ' ' + char *argv[MAXARGS]; // arguments to the command to be exec-ed +}; + +struct redircmd { + int type; // < or > + struct cmd *cmd; // the command to be run (e.g., an execcmd) + char *file; // the input/output file + int mode; // the mode to open the file with + int fd; // the file descriptor number to use for the file +}; + +struct pipecmd { + int type; // | + struct cmd *left; // left side of pipe + struct cmd *right; // right side of pipe +}; + +int fork1(void); // Fork but exits on failure. +struct cmd *parsecmd(char *); + +// Execute cmd. Never returns. +void runcmd(struct cmd *cmd) { + int p[2], r; + struct execcmd *ecmd; + struct pipecmd *pcmd; + struct redircmd *rcmd; + + if (cmd == 0) + exit(0); + + switch (cmd->type) { + default: + fprintf(stderr, "unknown runcmd\n"); + exit(-1); + + case ' ': + ecmd = (struct execcmd *)cmd; + if (ecmd->argv[0] == 0) + exit(0); + execv(ecmd->argv[0], ecmd->argv); + break; + + case '>': + case '<': + rcmd = (struct redircmd *)cmd; + fprintf(stderr, "redir not implemented\n"); + // Your code here ... + runcmd(rcmd->cmd); + break; + + case '|': + pcmd = (struct pipecmd *)cmd; + fprintf(stderr, "pipe not implemented\n"); + // Your code here ... + break; + } + exit(0); +} + +int getcmd(char *buf, int nbuf) { + + if (isatty(fileno(stdin))) + fprintf(stdout, "6.828$ "); + memset(buf, 0, nbuf); + fgets(buf, nbuf, stdin); + if (buf[0] == 0) // EOF + return -1; + return 0; +} + +int main(void) { + static char buf[100]; + int fd, r; + + // 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) + fprintf(stderr, "cannot cd %s\n", buf + 3); + continue; + } + if (fork1() == 0) + runcmd(parsecmd(buf)); + wait(&r); + } + exit(0); +} + +int fork1(void) { + int pid; + + pid = fork(); + if (pid == -1) + perror("fork"); + return pid; +} + +struct cmd *execcmd(void) { + struct execcmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = ' '; + return (struct cmd *)cmd; +} + +struct cmd *redircmd(struct cmd *subcmd, char *file, int type) { + struct redircmd *cmd; + + cmd = malloc(sizeof(*cmd)); + memset(cmd, 0, sizeof(*cmd)); + cmd->type = type; + cmd->cmd = subcmd; + cmd->file = file; + cmd->mode = (type == '<') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC; + cmd->fd = (type == '<') ? 0 : 1; + 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 = '|'; + cmd->left = left; + cmd->right = right; + return (struct cmd *)cmd; +} + +// 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 '<': + s++; + break; + case '>': + 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 *); + +// make a copy of the characters in the input buffer, starting from s through +// es. +// null-terminate the copy to make it a string. +char *mkcopy(char *s, char *es) { + int n = es - s; + char *c = malloc(n + 1); + assert(c); + strncpy(c, s, n); + c[n] = 0; + return c; +} + +struct cmd *parsecmd(char *s) { + char *es; + struct cmd *cmd; + + es = s + strlen(s); + cmd = parseline(&s, es); + peek(&s, es, ""); + if (s != es) { + fprintf(stderr, "leftovers: %s\n", s); + exit(-1); + } + return cmd; +} + +struct cmd *parseline(char **ps, char *es) { + struct cmd *cmd; + cmd = parsepipe(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') { + fprintf(stderr, "missing file for redirection\n"); + exit(-1); + } + switch (tok) { + case '<': + cmd = redircmd(cmd, mkcopy(q, eq), '<'); + break; + case '>': + cmd = redircmd(cmd, mkcopy(q, eq), '>'); + break; + } + } + return cmd; +} + +struct cmd *parseexec(char **ps, char *es) { + char *q, *eq; + int tok, argc; + struct execcmd *cmd; + struct cmd *ret; + + 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') { + fprintf(stderr, "syntax error\n"); + exit(-1); + } + cmd->argv[argc] = mkcopy(q, eq); + argc++; + if (argc >= MAXARGS) { + fprintf(stderr, "too many args\n"); + exit(-1); + } + ret = parseredirs(ret, ps, es); + } + cmd->argv[argc] = 0; // Add final NULL arg. See `man 2 exec` + return ret; +} diff --git a/hw/test.sh b/hw/test.sh new file mode 100644 index 0000000..cc348f9 --- /dev/null +++ b/hw/test.sh @@ -0,0 +1,6 @@ +ls > y +cat < y | sort | uniq | wc > y1 +cat y1 +rm y1 +ls | sort | uniq | wc +rm y