From 69629dbd351c7e0a555d70c7495a6f677cf06f92 Mon Sep 17 00:00:00 2001 From: We-unite <3205135446@qq.com> Date: Tue, 4 Jun 2024 23:34:31 +0800 Subject: Add simple redirection and it can run now - add redirect in/out/background, which is too simple - use associativity of bison to fix shift/reduce confliction - in bison, we can define associativity of token by this way: ```bison // in the first part %locations %left PLUS MINUS %left TIMES DEV %right ... %nonassoc ... ``` left or right means the order it is from ( we calculate lots of plus from the left ), while nonassoc means it has no associativity. --- Makefile | 36 ++++----------- bcsh.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- cmd.h | 19 ++++++-- lex.l | 8 ++-- syntax.y | 53 +++++++++++++--------- 5 files changed, 196 insertions(+), 76 deletions(-) diff --git a/Makefile b/Makefile index 8410a24..c052fe9 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,14 @@ -CC = gcc -FLEX = flex -BISON = bison +bcsh: syntax.tab.o bcsh.c + gcc -g -o bcsh bcsh.c syntax.tab.o -lfl -CFILES = $(shell find . -name "*.c") -OBJS = $(CFILES:.c=.o) -LFILE = $(wildcard *.l) -YFILE = $(wildcard *.y) -LFC = ./lex.yy.c -YFC = ./syntax.tab.c -LFO = $(LFC:.c=.o) -YFO = $(YFC:.c=.o) +syntax.tab.o: lex.yy.c syntax.tab.c + gcc -g -c syntax.tab.c -o syntax.tab.o -bcsh: syntax $(filter-out $(LFO),$(OBJS)) - $(CC) -g -o bcsh $(filter-out $(LFO),$(OBJS)) -lfl +lex.yy.c: lex.l + flex -o lex.yy.c lex.l -syntax: lexical syntax-c - $(CC) -c $(YFC) -o $(YFO) - -lexical: $(LFILE) - $(FLEX) -o $(LFC) $(LFILE) - -syntax-c: $(YFILE) - $(BISON) -o $(YFC) -d -v $(YFILE) -Wcounterexamples - --include $(patsubst %.o, %.d, $(OBJS)) +syntax.tab.c: syntax.y + bison -o syntax.tab.c -d -v syntax.y -Wcounterexamples clean: - rm -f bcsh lex.yy.c syntax.tab.c syntax.tab.h syntax.output - rm -f $(OBJS) $(OBJS:.o=.d) - rm -f $(LFC) $(YFC) $(YFC:.c=.h) - rm -f *~ \ No newline at end of file + rm -f bcsh lex.yy.c syntax.tab.c syntax.tab.h syntax.output syntax.tab.o diff --git a/bcsh.c b/bcsh.c index 6830671..08f7ac7 100644 --- a/bcsh.c +++ b/bcsh.c @@ -1,40 +1,161 @@ #include "cmd.h" extern int yyparse(); +char hostname[100]; +char *username; +char pwd[100]; int main(int argc, char **argv) { + // 获取当前登陆的用户名、系统主机名、当前工作路径 + gethostname(hostname, sizeof(hostname)); + getcwd(pwd, sizeof(pwd)); + username = getpwuid(getuid())->pw_name; + showPrompt(); while (true) { yyparse(); } return 0; } +void showPrompt() { + printf("\033[01;32m%s@%s\033[00m:\033[33m%s\033[00m$ ", username, hostname, + pwd); + // 强制刷新缓冲区 + fflush(stdout); +} + Command *newcmd() { Command *cmd = (Command *)malloc(sizeof(Command)); cmd->argc = 0; cmd->args = NULL; - cmd->type = CMD_TYPE_NORMAL; + cmd->type = CMD_NORMAL; cmd->left = NULL; cmd->right = NULL; + for (int i = 0; i < 3; i++) { + cmd->redirectFile[i] = NULL; + cmd->redirectFD[i] = i; + } + cmd->background = false; return cmd; } int runcmd(Command *cmd) { - int i; + int status; if (cmd == NULL) { fprintf(stderr, "cmd is NULL\n"); - } else if (cmd->type != CMD_TYPE_NORMAL) { - runcmd(cmd->left); - runcmd(cmd->right); - return 0; + return -1; } - printcmd(cmd); - if (!strcmp(cmd->args[0], "exit")) { - freecmd(cmd); - exit(0); + + if (cmd->type == CMD_NORMAL) { + pid_t pid; + if (!strcmp(cmd->args[0], "exit")) { + printf("Good Bye, %s!\n", username); + exit(0); + } else if (!strcmp(cmd->args[0], "cd")) { + if (cmd->argc == 1) { + chdir(getenv("HOME")); + } else { + chdir(cmd->args[1]); + } + printf("\r"); + getcwd(pwd, sizeof(pwd)); + } else { + if ((pid = fork()) == 0) { + // consider the redirection now + // it's just a part of normal command + // but not in other types + for (int i = 0; i < 3; i++) { + if (cmd->redirectFD[i] != i) { + close(i); + dup2(cmd->redirectFD[i], i); + } else if (cmd->redirectFile[i] != NULL) { + int fd = + open(cmd->redirectFile[i], O_RDWR | O_CREAT, 0666); + if (fd < 0) { + fprintf(stderr, "open file %s failed\n", + cmd->redirectFile[i]); + exit(-1); + } + close(i); + dup2(fd, i); + close(fd); + } + } + if (cmd->background) { + setpgid(0, 0); + // no output, redirect 1 and 2 to /dev/null + int fd = open("/dev/null", O_RDWR); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + } + exit(execvp(cmd->args[0], cmd->args)); + } else { + if (!cmd->background) { + waitpid(pid, &status, 0); + if (!WIFEXITED(status)) { + // when here, the child process exited abnormally + fprintf(stderr, "Process %d exited with status %d\n", + pid, WEXITSTATUS(status)); + } + } + } + // return the exit status of the child process + return WEXITSTATUS(status); + } + } else if (cmd->type == CMD_PIPE) { + // create a pipe file and gets two file descriptors + // fd[0] is the read end, while fd[1] the write end + int fd[2]; + pid_t left, right; + pipe(fd); + if ((left = fork()) == 0) { + /* + * close the read end of the pipe, so that the child process + * can only write to it; then redirect the standard output + * to the write end of the pipe, and close the write end + * itself as a file descriptor. + */ + close(fd[0]); + dup2(fd[1], 1); + close(fd[1]); + exit(runcmd(cmd->left)); + } + if ((right = fork()) == 0) { + close(fd[1]); + dup2(fd[0], 0); + close(fd[0]); + exit(runcmd(cmd->right)); + } + // close the pipe, because the parent process doesn't need it + // and wait for the two child processes to exit + close(fd[0]); + close(fd[1]); + waitpid(left, NULL, 0); + waitpid(right, NULL, 0); + } else if (cmd->type == CMD_AND) { + if (fork() == 0) { + exit(runcmd(cmd->left)); + } else { + waitpid(-1, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && fork() == 0) { + status = runcmd(cmd->right); + } + wait(NULL); + } + } else if (cmd->type == CMD_OR) { + if (fork() == 0) { + exit(runcmd(cmd->left)); + } else { + waitpid(-1, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) != 0 && fork() == 0) { + status = runcmd(cmd->right); + } + wait(NULL); + } } freecmd(cmd); - return 0; + return status; } void freecmd(Command *cmd) { @@ -52,13 +173,10 @@ void freecmd(Command *cmd) { free(cmd->args[i]); } free(cmd->args); - free(cmd); -} - -void printcmd(Command *cmd) { - int i; - for (i = 0; i < cmd->argc; i++) { - printf("%s ", cmd->args[i]); + for (i = 0; i < 3; i++) { + if (cmd->redirectFile[i] != NULL) { + free(cmd->redirectFile[i]); + } } - printf("\n"); + free(cmd); } \ No newline at end of file diff --git a/cmd.h b/cmd.h index 38b3fa1..d591111 100644 --- a/cmd.h +++ b/cmd.h @@ -1,16 +1,21 @@ #ifndef CMD_H #define CMD_H +#include #include #include #include #include +#include +#include +#include +#include typedef enum { - CMD_TYPE_NORMAL, - CMD_TYPE_PIPE, - CMD_TYPE_AND, - CMD_TYPE_OR, + CMD_NORMAL, + CMD_PIPE, + CMD_AND, + CMD_OR, // add more command types as needed } CommandType; @@ -20,9 +25,13 @@ typedef struct Command { int argc; struct Command *left; // a pointer to the left sub-command struct Command *right; // a pointer to the right sub-command + int redirectFD[3]; + char *redirectFile[3]; + bool background; } Command; -Command* newcmd(); +void showPrompt(); +Command *newcmd(); int runcmd(Command *cmd); void freecmd(Command *cmd); void printcmd(Command *cmd); diff --git a/lex.l b/lex.l index c530ca0..3b99f1c 100644 --- a/lex.l +++ b/lex.l @@ -14,9 +14,11 @@ void yyerror(const char *s); "|" { return PIPE; } "&&" { return AND; } "||" { return OR; } -\"(\\.|[^\"])*\" { yylval.str = strdup(yytext); return STRING; } -[a-zA-Z0-9_\-\/.]+ { yylval.str = strdup(yytext); return WORD; } - +"<" { return REDIRECT_IN; } +">" { return REDIRECT_OUT; } +"&" { return BACKGROUND; } +[0-2]">"&[0-2] { yylval.str = strdup(yytext); return FD_REDIRECT; } +[a-zA-Z0-9_\-\/\*.]+ { yylval.str = strdup(yytext); return WORD; } %% diff --git a/syntax.y b/syntax.y index d9d3510..5f33c9a 100644 --- a/syntax.y +++ b/syntax.y @@ -8,14 +8,20 @@ char *str; struct Command *cmd; CommandType cmdType; + int fd; } -%token WORD STRING error -%token NEWLINE PIPE AND OR +%token WORD STRING error FD_REDIRECT +%token NEWLINE PIPE AND OR REDIRECT_IN REDIRECT_OUT BACKGROUND %type line command part runcommand %type separator +%locations +%nonassoc WORD +%nonassoc BACKGROUND +%nonassoc REDIRECT_IN REDIRECT_OUT + %% line: @@ -26,8 +32,8 @@ line: runcommand: command NEWLINE { $$ = $1; - printf("running command: \n"); runcmd($$); + showPrompt(); } | NEWLINE { $$ = NULL; } ; @@ -44,18 +50,17 @@ command: } ; +separator: + PIPE { $$ = CMD_PIPE; } + | AND { $$ = CMD_AND; } + | OR { $$ = CMD_OR; } + ; + part: part WORD { $$ = $1; $$->argc++; - $$->args = realloc($$->args, ($$->argc) * sizeof(char *)); - $$->args[$$->argc - 1] = $2; - $$->args[$$->argc] = NULL; - } - | part STRING { - $$ = $1; - $$->argc++; - $$->args = realloc($$->args, ($$->argc) * sizeof(char *)); + $$->args = realloc($$->args, ($$->argc + 1) * sizeof(char *)); $$->args[$$->argc - 1] = $2; $$->args[$$->argc] = NULL; } @@ -65,18 +70,22 @@ part: $$->args[$$->argc++] = $1; $$->args[$$->argc] = NULL; } - | STRING { - $$ = newcmd(); - $$->args = malloc(2 * sizeof(char *)); - $$->args[$$->argc++] = $1; - $$->args[$$->argc] = NULL; + | part REDIRECT_IN WORD { + $$ = $1; + $$->redirectFile[0] = $3; + } + | part REDIRECT_OUT WORD { + $$ = $1; + $$->redirectFile[1] = $3; + } + | part FD_REDIRECT { + $$ = $1; + $$->redirectFD[$2[0]-'0']=$2[3]-'0'; + } + | part BACKGROUND { + $$ = $1; + $$->background = true; } - ; - -separator: - PIPE { $$ = CMD_TYPE_PIPE; } - | AND { $$ = CMD_TYPE_AND; } - | OR { $$ = CMD_TYPE_OR; } ; %% -- cgit v1.2.3-70-g09d2