diff options
Diffstat (limited to 'cmd.c')
-rw-r--r-- | cmd.c | 195 |
1 files changed, 195 insertions, 0 deletions
@@ -0,0 +1,195 @@ | |||
1 | #include "cmd.h" | ||
2 | |||
3 | Command *newCmd() { | ||
4 | Command *cmd = (Command *)malloc(sizeof(Command)); | ||
5 | cmd->argc = 0; | ||
6 | cmd->args = NULL; | ||
7 | cmd->type = CMD_NORMAL; | ||
8 | cmd->left = NULL; | ||
9 | cmd->right = NULL; | ||
10 | for (int i = 0; i < 3; i++) { | ||
11 | cmd->redirectFile[i] = NULL; | ||
12 | cmd->redirectFD[i] = i; | ||
13 | } | ||
14 | cmd->background = false; | ||
15 | return cmd; | ||
16 | } | ||
17 | |||
18 | void freeCmd(Command *cmd) { | ||
19 | if (cmd == NULL) { | ||
20 | return; | ||
21 | } | ||
22 | if (cmd->left != NULL) { | ||
23 | freeCmd(cmd->left); | ||
24 | } | ||
25 | if (cmd->right != NULL) { | ||
26 | freeCmd(cmd->right); | ||
27 | } | ||
28 | int i; | ||
29 | for (i = 0; i < cmd->argc; i++) { | ||
30 | free(cmd->args[i]); | ||
31 | } | ||
32 | free(cmd->args); | ||
33 | for (i = 0; i < 3; i++) { | ||
34 | if (cmd->redirectFile[i] != NULL) { | ||
35 | free(cmd->redirectFile[i]); | ||
36 | } | ||
37 | } | ||
38 | free(cmd); | ||
39 | } | ||
40 | |||
41 | int runCmd(Command *cmd) { | ||
42 | int status; | ||
43 | if (cmd == NULL) { | ||
44 | fprintf(stderr, "cmd is NULL\n"); | ||
45 | return -1; | ||
46 | } | ||
47 | switch (cmd->type) { | ||
48 | case CMD_NORMAL: | ||
49 | status = runNormalCmd(cmd); | ||
50 | break; | ||
51 | case CMD_PIPE: | ||
52 | status = runPipeCmd(cmd); | ||
53 | break; | ||
54 | case CMD_AND: | ||
55 | case CMD_OR: | ||
56 | status = runAndOrCmd(cmd); | ||
57 | break; | ||
58 | } | ||
59 | |||
60 | freeCmd(cmd); | ||
61 | return status; | ||
62 | } | ||
63 | |||
64 | int runNormalCmd(Command *cmd) { | ||
65 | pid_t pid; | ||
66 | int status; | ||
67 | if (!strcmp(cmd->args[0], "exit")) { | ||
68 | printf("Good Bye, %s!\n", username); | ||
69 | exit(0); | ||
70 | } else if (!strcmp(cmd->args[0], "cd")) { | ||
71 | if (cmd->argc == 1) { | ||
72 | chdir(getenv("HOME")); | ||
73 | } else { | ||
74 | chdir(cmd->args[1]); | ||
75 | } | ||
76 | printf("\r"); | ||
77 | getcwd(pwd, sizeof(pwd)); | ||
78 | } else { | ||
79 | if ((pid = fork()) == 0) { | ||
80 | // consider the redirection now | ||
81 | // it's just a part of normal command | ||
82 | // but not in other commands | ||
83 | redirect(cmd); | ||
84 | exit(execvp(cmd->args[0], cmd->args)); | ||
85 | } else { | ||
86 | if (!cmd->background) { | ||
87 | waitpid(pid, &status, 0); | ||
88 | if (!WIFEXITED(status)) { | ||
89 | if (WIFSIGNALED(status)) { | ||
90 | fprintf(stderr, "Process %d was killed by signal %d\n", | ||
91 | pid, WTERMSIG(status)); | ||
92 | } else { | ||
93 | fprintf(stderr, "Process %d exited with status %d\n", | ||
94 | pid, WEXITSTATUS(status)); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | // return the exit status of the child process | ||
100 | return WEXITSTATUS(status); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | int runPipeCmd(Command *cmd) { | ||
105 | int status; | ||
106 | // create a pipe file and gets two file descriptors | ||
107 | // fd[0] is the read end, while fd[1] the write end | ||
108 | int fd[2]; | ||
109 | pid_t left, right; | ||
110 | pipe(fd); | ||
111 | if ((left = fork()) == 0) { | ||
112 | /* | ||
113 | * close the read end of the pipe, so that the child process | ||
114 | * can only write to it; then redirect the standard output | ||
115 | * to the write end of the pipe, and close the write end | ||
116 | * itself as a file descriptor. | ||
117 | */ | ||
118 | close(fd[0]); | ||
119 | dup2(fd[1], 1); | ||
120 | close(fd[1]); | ||
121 | exit(runCmd(cmd->left)); | ||
122 | } | ||
123 | if ((right = fork()) == 0) { | ||
124 | close(fd[1]); | ||
125 | dup2(fd[0], 0); | ||
126 | close(fd[0]); | ||
127 | exit(runCmd(cmd->right)); | ||
128 | } | ||
129 | // close the pipe, because the parent process doesn't need it | ||
130 | // and wait for the two child processes to exit | ||
131 | close(fd[0]); | ||
132 | close(fd[1]); | ||
133 | waitpid(left, NULL, 0); | ||
134 | waitpid(right, NULL, 0); | ||
135 | } | ||
136 | |||
137 | int runAndOrCmd(Command *cmd) { | ||
138 | int status; | ||
139 | if (cmd->type == CMD_AND) { | ||
140 | if (fork() == 0) { | ||
141 | exit(runCmd(cmd->left)); | ||
142 | } else { | ||
143 | waitpid(-1, &status, 0); | ||
144 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && fork() == 0) { | ||
145 | status = runCmd(cmd->right); | ||
146 | } | ||
147 | wait(NULL); | ||
148 | } | ||
149 | } else if (cmd->type == CMD_OR) { | ||
150 | if (fork() == 0) { | ||
151 | exit(runCmd(cmd->left)); | ||
152 | } else { | ||
153 | waitpid(-1, &status, 0); | ||
154 | if (WIFEXITED(status) && WEXITSTATUS(status) != 0 && fork() == 0) { | ||
155 | status = runCmd(cmd->right); | ||
156 | } | ||
157 | wait(NULL); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | void redirect(Command *cur) { | ||
163 | int fd; | ||
164 | char *rfile; | ||
165 | for (int i = 0; i < 3; i++) { | ||
166 | fd = cur->redirectFD[i]; | ||
167 | rfile = cur->redirectFile[i]; | ||
168 | if (fd < -1 || fd > 2) { | ||
169 | fprintf(stderr, "file descriptor %d is invalid\n", fd); | ||
170 | exit(1); | ||
171 | } | ||
172 | if (fd == i && rfile == NULL) { | ||
173 | continue; | ||
174 | } | ||
175 | if (fd == -1) { | ||
176 | // append | ||
177 | fd = open(rfile, O_WRONLY | O_CREAT | O_APPEND, 0666); | ||
178 | } else if (rfile != NULL) { | ||
179 | // normal redirection | ||
180 | fd = open(rfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); | ||
181 | } else { | ||
182 | // file descriptor redirection | ||
183 | // when here, fd is surely in 0, 1, 2 | ||
184 | ; | ||
185 | } | ||
186 | |||
187 | if (fd < 0) { | ||
188 | fprintf(stderr, "open file %s failed\n", rfile); | ||
189 | exit(1); | ||
190 | } | ||
191 | close(i); | ||
192 | dup2(fd, i); | ||
193 | close(fd); | ||
194 | } | ||
195 | } \ No newline at end of file | ||