Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newhw1 Student 10 #92

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions 10/01/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build:
gcc sh.c -o sh
clean:
rm -f sh
384 changes: 384 additions & 0 deletions 10/01/sh.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,384 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

// 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)
{
// p - read/write pipe, r - file descriptor
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);
// V: as an array of char*
// The versions with 'p' in there use the environment path variable to search for the executable file named to execute.
if(execvp(ecmd->argv[0], ecmd->argv) == -1)
{
fprintf(stderr, "can't exec: %s\n", strerror(errno));
}
break;

case '>':
case '<':
rcmd = (struct redircmd*)cmd;
// Open file
r = open(rcmd->file, rcmd->mode);
if (r==-1) {
fprintf(stderr, "can't open file: %s\n", strerror(errno));
exit(-1);
}
// Duplicate file descriptor to stdin (for '<') or stdout (for '>') descriptor
if (dup2(r, rcmd->fd)==-1) {
fprintf(stderr, "can't copy descriptor: %s\n", strerror(errno));
exit(-1);
}
// Close file
if (close(r)==-1) {
fprintf(stderr, "can't close: %s\n", strerror(errno));
exit(-1);
}
runcmd(rcmd->cmd);
break;

case '|':
pcmd = (struct pipecmd*)cmd;
fprintf(stderr, "pipe not implemented\n");
// Creating a pipe. Pipe is the two elements array, where p[0] is read descriptor and p[1] - write descriptor
if (pipe(p) == -1) {
fprintf(stderr, "can't create pipe: %s\n", strerror(errno));
exit(-1);
}
struct cmd *pipepart;
// If fork1() == 0 then work with child process, else work with parent process
if (fork1() == 0) {
// Child process works with stdin and choose right side of pipe command
if (dup2(p[0], fileno(stdout))==-1) {
fprintf(stderr, "can't copy descriptor: %s\n", strerror(errno));
exit(-1);
}
pipepart = pcmd->right;
} else {
// Parent process works with stdout and choose left side of pipe command
if (dup2(p[1], fileno(stdin))==-1) {
fprintf(stderr, "can't copy descriptor: %s\n", strerror(errno));
exit(-1);
}
pipepart = pcmd->left;
}
// Close pipes
if (close(p[0])==-1) {
fprintf(stderr, "can't close read descriptor: %s\n", strerror(errno));
exit(-1);
}
if (close(p[1])==-1) {
fprintf(stderr, "can't close write descriptor: %s\n", strerror(errno));
exit(-1);
}
// Run left or right side of pipe command
runcmd(pipepart);
break;
}
exit(0);
}

int
getcmd(char *buf, int nbuf)
{

if (isatty(fileno(stdin)))
fprintf(stdout, "$ ");
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;
return ret;
}