aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2024-06-04 23:34:31 +0800
committerWe-unite <3205135446@qq.com>2024-06-06 00:41:36 +0800
commit69629dbd351c7e0a555d70c7495a6f677cf06f92 (patch)
tree28d8223e2f08fa345cb4ecc1215b7186b96eeaf8
parent2501c46c383bf4a113667db9d372d45aee2cd695 (diff)
downloadbcsh-69629dbd351c7e0a555d70c7495a6f677cf06f92.tar.gz
bcsh-69629dbd351c7e0a555d70c7495a6f677cf06f92.zip
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.
-rw-r--r--Makefile36
-rw-r--r--bcsh.c156
-rw-r--r--cmd.h19
-rw-r--r--lex.l8
-rw-r--r--syntax.y53
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 @@
1CC = gcc 1bcsh: syntax.tab.o bcsh.c
2FLEX = flex 2 gcc -g -o bcsh bcsh.c syntax.tab.o -lfl
3BISON = bison
4 3
5CFILES = $(shell find . -name "*.c") 4syntax.tab.o: lex.yy.c syntax.tab.c
6OBJS = $(CFILES:.c=.o) 5 gcc -g -c syntax.tab.c -o syntax.tab.o
7LFILE = $(wildcard *.l)
8YFILE = $(wildcard *.y)
9LFC = ./lex.yy.c
10YFC = ./syntax.tab.c
11LFO = $(LFC:.c=.o)
12YFO = $(YFC:.c=.o)
13 6
14bcsh: syntax $(filter-out $(LFO),$(OBJS)) 7lex.yy.c: lex.l
15 $(CC) -g -o bcsh $(filter-out $(LFO),$(OBJS)) -lfl 8 flex -o lex.yy.c lex.l
16 9
17syntax: lexical syntax-c 10syntax.tab.c: syntax.y
18 $(CC) -c $(YFC) -o $(YFO) 11 bison -o syntax.tab.c -d -v syntax.y -Wcounterexamples
19
20lexical: $(LFILE)
21 $(FLEX) -o $(LFC) $(LFILE)
22
23syntax-c: $(YFILE)
24 $(BISON) -o $(YFC) -d -v $(YFILE) -Wcounterexamples
25
26-include $(patsubst %.o, %.d, $(OBJS))
27 12
28clean: 13clean:
29 rm -f bcsh lex.yy.c syntax.tab.c syntax.tab.h syntax.output 14 rm -f bcsh lex.yy.c syntax.tab.c syntax.tab.h syntax.output syntax.tab.o
30 rm -f $(OBJS) $(OBJS:.o=.d)
31 rm -f $(LFC) $(YFC) $(YFC:.c=.h)
32 rm -f *~ \ No newline at end of file
diff --git a/bcsh.c b/bcsh.c
index 6830671..08f7ac7 100644
--- a/bcsh.c
+++ b/bcsh.c
@@ -1,40 +1,161 @@
1#include "cmd.h" 1#include "cmd.h"
2 2
3extern int yyparse(); 3extern int yyparse();
4char hostname[100];
5char *username;
6char pwd[100];
4 7
5int main(int argc, char **argv) { 8int main(int argc, char **argv) {
9 // 获取当前登陆的用户名、系统主机名、当前工作路径
10 gethostname(hostname, sizeof(hostname));
11 getcwd(pwd, sizeof(pwd));
12 username = getpwuid(getuid())->pw_name;
13 showPrompt();
6 while (true) { 14 while (true) {
7 yyparse(); 15 yyparse();
8 } 16 }
9 return 0; 17 return 0;
10} 18}
11 19
20void showPrompt() {
21 printf("\033[01;32m%s@%s\033[00m:\033[33m%s\033[00m$ ", username, hostname,
22 pwd);
23 // 强制刷新缓冲区
24 fflush(stdout);
25}
26
12Command *newcmd() { 27Command *newcmd() {
13 Command *cmd = (Command *)malloc(sizeof(Command)); 28 Command *cmd = (Command *)malloc(sizeof(Command));
14 cmd->argc = 0; 29 cmd->argc = 0;
15 cmd->args = NULL; 30 cmd->args = NULL;
16 cmd->type = CMD_TYPE_NORMAL; 31 cmd->type = CMD_NORMAL;
17 cmd->left = NULL; 32 cmd->left = NULL;
18 cmd->right = NULL; 33 cmd->right = NULL;
34 for (int i = 0; i < 3; i++) {
35 cmd->redirectFile[i] = NULL;
36 cmd->redirectFD[i] = i;
37 }
38 cmd->background = false;
19 return cmd; 39 return cmd;
20} 40}
21 41
22int runcmd(Command *cmd) { 42int runcmd(Command *cmd) {
23 int i; 43 int status;
24 if (cmd == NULL) { 44 if (cmd == NULL) {
25 fprintf(stderr, "cmd is NULL\n"); 45 fprintf(stderr, "cmd is NULL\n");
26 } else if (cmd->type != CMD_TYPE_NORMAL) { 46 return -1;
27 runcmd(cmd->left);
28 runcmd(cmd->right);
29 return 0;
30 } 47 }
31 printcmd(cmd); 48
32 if (!strcmp(cmd->args[0], "exit")) { 49 if (cmd->type == CMD_NORMAL) {
33 freecmd(cmd); 50 pid_t pid;
34 exit(0); 51 if (!strcmp(cmd->args[0], "exit")) {
52 printf("Good Bye, %s!\n", username);
53 exit(0);
54 } else if (!strcmp(cmd->args[0], "cd")) {
55 if (cmd->argc == 1) {
56 chdir(getenv("HOME"));
57 } else {
58 chdir(cmd->args[1]);
59 }
60 printf("\r");
61 getcwd(pwd, sizeof(pwd));
62 } else {
63 if ((pid = fork()) == 0) {
64 // consider the redirection now
65 // it's just a part of normal command
66 // but not in other types
67 for (int i = 0; i < 3; i++) {
68 if (cmd->redirectFD[i] != i) {
69 close(i);
70 dup2(cmd->redirectFD[i], i);
71 } else if (cmd->redirectFile[i] != NULL) {
72 int fd =
73 open(cmd->redirectFile[i], O_RDWR | O_CREAT, 0666);
74 if (fd < 0) {
75 fprintf(stderr, "open file %s failed\n",
76 cmd->redirectFile[i]);
77 exit(-1);
78 }
79 close(i);
80 dup2(fd, i);
81 close(fd);
82 }
83 }
84 if (cmd->background) {
85 setpgid(0, 0);
86 // no output, redirect 1 and 2 to /dev/null
87 int fd = open("/dev/null", O_RDWR);
88 dup2(fd, 1);
89 dup2(fd, 2);
90 close(fd);
91 }
92 exit(execvp(cmd->args[0], cmd->args));
93 } else {
94 if (!cmd->background) {
95 waitpid(pid, &status, 0);
96 if (!WIFEXITED(status)) {
97 // when here, the child process exited abnormally
98 fprintf(stderr, "Process %d exited with status %d\n",
99 pid, WEXITSTATUS(status));
100 }
101 }
102 }
103 // return the exit status of the child process
104 return WEXITSTATUS(status);
105 }
106 } else if (cmd->type == CMD_PIPE) {
107 // create a pipe file and gets two file descriptors
108 // fd[0] is the read end, while fd[1] the write end
109 int fd[2];
110 pid_t left, right;
111 pipe(fd);
112 if ((left = fork()) == 0) {
113 /*
114 * close the read end of the pipe, so that the child process
115 * can only write to it; then redirect the standard output
116 * to the write end of the pipe, and close the write end
117 * itself as a file descriptor.
118 */
119 close(fd[0]);
120 dup2(fd[1], 1);
121 close(fd[1]);
122 exit(runcmd(cmd->left));
123 }
124 if ((right = fork()) == 0) {
125 close(fd[1]);
126 dup2(fd[0], 0);
127 close(fd[0]);
128 exit(runcmd(cmd->right));
129 }
130 // close the pipe, because the parent process doesn't need it
131 // and wait for the two child processes to exit
132 close(fd[0]);
133 close(fd[1]);
134 waitpid(left, NULL, 0);
135 waitpid(right, NULL, 0);
136 } else if (cmd->type == CMD_AND) {
137 if (fork() == 0) {
138 exit(runcmd(cmd->left));
139 } else {
140 waitpid(-1, &status, 0);
141 if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && fork() == 0) {
142 status = runcmd(cmd->right);
143 }
144 wait(NULL);
145 }
146 } else if (cmd->type == CMD_OR) {
147 if (fork() == 0) {
148 exit(runcmd(cmd->left));
149 } else {
150 waitpid(-1, &status, 0);
151 if (WIFEXITED(status) && WEXITSTATUS(status) != 0 && fork() == 0) {
152 status = runcmd(cmd->right);
153 }
154 wait(NULL);
155 }
35 } 156 }
36 freecmd(cmd); 157 freecmd(cmd);
37 return 0; 158 return status;
38} 159}
39 160
40void freecmd(Command *cmd) { 161void freecmd(Command *cmd) {
@@ -52,13 +173,10 @@ void freecmd(Command *cmd) {
52 free(cmd->args[i]); 173 free(cmd->args[i]);
53 } 174 }
54 free(cmd->args); 175 free(cmd->args);
55 free(cmd); 176 for (i = 0; i < 3; i++) {
56} 177 if (cmd->redirectFile[i] != NULL) {
57 178 free(cmd->redirectFile[i]);
58void printcmd(Command *cmd) { 179 }
59 int i;
60 for (i = 0; i < cmd->argc; i++) {
61 printf("%s ", cmd->args[i]);
62 } 180 }
63 printf("\n"); 181 free(cmd);
64} \ No newline at end of file 182} \ 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 @@
1#ifndef CMD_H 1#ifndef CMD_H
2#define CMD_H 2#define CMD_H
3 3
4#include <pwd.h>
4#include <stdbool.h> 5#include <stdbool.h>
5#include <stdio.h> 6#include <stdio.h>
6#include <stdlib.h> 7#include <stdlib.h>
7#include <string.h> 8#include <string.h>
9#include <sys/types.h>
10#include <sys/wait.h>
11#include <unistd.h>
12#include <fcntl.h>
8 13
9typedef enum { 14typedef enum {
10 CMD_TYPE_NORMAL, 15 CMD_NORMAL,
11 CMD_TYPE_PIPE, 16 CMD_PIPE,
12 CMD_TYPE_AND, 17 CMD_AND,
13 CMD_TYPE_OR, 18 CMD_OR,
14 // add more command types as needed 19 // add more command types as needed
15} CommandType; 20} CommandType;
16 21
@@ -20,9 +25,13 @@ typedef struct Command {
20 int argc; 25 int argc;
21 struct Command *left; // a pointer to the left sub-command 26 struct Command *left; // a pointer to the left sub-command
22 struct Command *right; // a pointer to the right sub-command 27 struct Command *right; // a pointer to the right sub-command
28 int redirectFD[3];
29 char *redirectFile[3];
30 bool background;
23} Command; 31} Command;
24 32
25Command* newcmd(); 33void showPrompt();
34Command *newcmd();
26int runcmd(Command *cmd); 35int runcmd(Command *cmd);
27void freecmd(Command *cmd); 36void freecmd(Command *cmd);
28void printcmd(Command *cmd); 37void 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);
14"|" { return PIPE; } 14"|" { return PIPE; }
15"&&" { return AND; } 15"&&" { return AND; }
16"||" { return OR; } 16"||" { return OR; }
17\"(\\.|[^\"])*\" { yylval.str = strdup(yytext); return STRING; } 17"<" { return REDIRECT_IN; }
18[a-zA-Z0-9_\-\/.]+ { yylval.str = strdup(yytext); return WORD; } 18">" { return REDIRECT_OUT; }
19 19"&" { return BACKGROUND; }
20[0-2]">"&[0-2] { yylval.str = strdup(yytext); return FD_REDIRECT; }
21[a-zA-Z0-9_\-\/\*.]+ { yylval.str = strdup(yytext); return WORD; }
20 22
21%% 23%%
22 24
diff --git a/syntax.y b/syntax.y
index d9d3510..5f33c9a 100644
--- a/syntax.y
+++ b/syntax.y
@@ -8,14 +8,20 @@
8 char *str; 8 char *str;
9 struct Command *cmd; 9 struct Command *cmd;
10 CommandType cmdType; 10 CommandType cmdType;
11 int fd;
11} 12}
12 13
13%token <str> WORD STRING error 14%token <str> WORD STRING error FD_REDIRECT
14%token NEWLINE PIPE AND OR 15%token NEWLINE PIPE AND OR REDIRECT_IN REDIRECT_OUT BACKGROUND
15 16
16%type <cmd> line command part runcommand 17%type <cmd> line command part runcommand
17%type <cmdType> separator 18%type <cmdType> separator
18 19
20%locations
21%nonassoc WORD
22%nonassoc BACKGROUND
23%nonassoc REDIRECT_IN REDIRECT_OUT
24
19%% 25%%
20 26
21line: 27line:
@@ -26,8 +32,8 @@ line:
26runcommand: 32runcommand:
27 command NEWLINE { 33 command NEWLINE {
28 $$ = $1; 34 $$ = $1;
29 printf("running command: \n");
30 runcmd($$); 35 runcmd($$);
36 showPrompt();
31 } 37 }
32 | NEWLINE { $$ = NULL; } 38 | NEWLINE { $$ = NULL; }
33 ; 39 ;
@@ -44,18 +50,17 @@ command:
44 } 50 }
45 ; 51 ;
46 52
53separator:
54 PIPE { $$ = CMD_PIPE; }
55 | AND { $$ = CMD_AND; }
56 | OR { $$ = CMD_OR; }
57 ;
58
47part: 59part:
48 part WORD { 60 part WORD {
49 $$ = $1; 61 $$ = $1;
50 $$->argc++; 62 $$->argc++;
51 $$->args = realloc($$->args, ($$->argc) * sizeof(char *)); 63 $$->args = realloc($$->args, ($$->argc + 1) * sizeof(char *));
52 $$->args[$$->argc - 1] = $2;
53 $$->args[$$->argc] = NULL;
54 }
55 | part STRING {
56 $$ = $1;
57 $$->argc++;
58 $$->args = realloc($$->args, ($$->argc) * sizeof(char *));
59 $$->args[$$->argc - 1] = $2; 64 $$->args[$$->argc - 1] = $2;
60 $$->args[$$->argc] = NULL; 65 $$->args[$$->argc] = NULL;
61 } 66 }
@@ -65,18 +70,22 @@ part:
65 $$->args[$$->argc++] = $1; 70 $$->args[$$->argc++] = $1;
66 $$->args[$$->argc] = NULL; 71 $$->args[$$->argc] = NULL;
67 } 72 }
68 | STRING { 73 | part REDIRECT_IN WORD {
69 $$ = newcmd(); 74 $$ = $1;
70 $$->args = malloc(2 * sizeof(char *)); 75 $$->redirectFile[0] = $3;
71 $$->args[$$->argc++] = $1; 76 }
72 $$->args[$$->argc] = NULL; 77 | part REDIRECT_OUT WORD {
78 $$ = $1;
79 $$->redirectFile[1] = $3;
80 }
81 | part FD_REDIRECT {
82 $$ = $1;
83 $$->redirectFD[$2[0]-'0']=$2[3]-'0';
84 }
85 | part BACKGROUND {
86 $$ = $1;
87 $$->background = true;
73 } 88 }
74 ;
75
76separator:
77 PIPE { $$ = CMD_TYPE_PIPE; }
78 | AND { $$ = CMD_TYPE_AND; }
79 | OR { $$ = CMD_TYPE_OR; }
80 ; 89 ;
81 90
82%% 91%%