-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmyshell.c
More file actions
295 lines (245 loc) · 10 KB
/
myshell.c
File metadata and controls
295 lines (245 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*
Name - Jonathan Mathurin
Panther ID - 6169303
I affirm that I wrote this program myself without any help from any other
people or sources from the internet.
------------------------------------------------
This program is intended to demonstrate how to use I/O redirections in C.
*/
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#define MAX_ARGS 20
#define BUFSIZE 1024
/* A simple function utilized to populate an argument. */
void pop_arg(char** arg1, char** arg2, int index) {
printf("arg1 = %s", arg1[0]);
for(int o = 0; o < index; o++) {
arg2[o] = arg1[o];
if (o == (index - 1)) {
arg2[o + 1] = 0;
}
}
}
int get_args(char* cmdline, char* args[])
{
int i = 0;
/* if no args */
if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
return 0;
while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
if(i >= MAX_ARGS) {
printf("Too many arguments!\n");
exit(1);
}
}
/* the last one is always NULL */
return i;
}
void execute(char* cmdline) {
/* FileId is a variable I added to ensure that > overwriting worked as intended.*/
int pid, async, fileId;
/* Input - A variables to store the fd for input file
* Output - Stores the fd for output file
* sub_args - A new list meant to store commands as I trim args down. */
int Input, Output, numOfPipes = 0;
char *args[MAX_ARGS];
char *sub_args[MAX_ARGS];
int nargs = get_args(cmdline, args);
if (nargs <= 0) return;
if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
exit(0);
}
/* check if async call */
if (!strcmp(args[nargs - 1], "&")) {
async = 1;
args[--nargs] = 0;
} else async = 0;
/* A simple for loop used to identify if our commands will have pipes.
* If it does have pipes, we keep track of the total number for future use. */
for (int i = 0; i < nargs; i++) {
if (!strcmp(args[i], "|")) {
numOfPipes++;
}
}
/* If we have any pipes, we're going to run an operation with pipes in mind. */
if (numOfPipes > 0) {
int pid, status;
int fd[2];
/* This is just a mechanism we use to get the input and outputs*/
for (int i = 0; i < nargs; i++) {
/* If we find the output operator, we know that the next item in the list
* will be name of the output file to be created/overwritten. */
if (!strcmp(args[i], ">")) {
//This stores the name into output.
//Creat ensures that the file is overwritten.
Output = creat(args[i + 1], 0640);
}
/* If we find the input operator, we know that the next item in the list
* will be name of the input file to be used as an input. */
if (!strcmp(args[i], "<")) {
//Stores name into input and makes it read only.
Input = open(args[i + 1], O_RDONLY, 0666);
}
}
pipe(fd);
switch (pid = fork()) {
case 0: /* child */
dup2(fd[1], 1); /* this end of the pipe becomes the standard output */
close(fd[0]); /* this process does not need the other end */
/* Run a loop from the beginning of the array, until the end. */
for (int i = 0; i < nargs; i++) {
//Once e find the pipe, we can begin the operations.
if (!strcmp(args[i], "|")){
/* We add to a sub_arg list from the beginning up until the pipe.*/
for (int o = 0; o < i; o++) {
sub_args[o] = args[o];
if (o == (i - 1)) {
//Add null character to end.
sub_args[o + 1] = 0;
}
}
}
}
execvp(sub_args[0], sub_args); /* run the command */
fprintf(stderr, "%s failed\n", sub_args[0]); /* commandt failed! */
default: /* parent does nothing */
break;
case -1:
fprintf(stderr, "fork failed\n");
exit(1);
}
switch (pid = fork()) {
case 0: /* child */
dup2(fd[0], 0); /* this end of the pipe becomes the standard input */
close(fd[1]); /* this process doesn't need the other end */
for (int i = 0; i < nargs; i++) {
if (!strcmp(args[i], "|")){
/* Once we locate the pipe, we focus on the opposite end as we did before.
* Hence the loop looks beyond the pipe and goes to the end of the argument.*/
int index_1 = 0;
for (int n = i+1; n < nargs; n++) {
sub_args[index_1] = args[n];
if (n == (nargs - 1)) {
sub_args[index_1 + 1] = 0;
}
index_1++;
}
}
}
execvp(sub_args[0], sub_args); /* run the command */
fprintf(stderr, "%s failed\n", sub_args[0]); /* it failed! */
default: /* parent does nothing */
break;
case -1:
fprintf(stderr, "fork failed\n");
exit(1);
}
close(fd[0]);
close(fd[1]); /* this is important! close both file descriptors on the pipe */
while ((pid = wait(&status)) != -1);
//fprintf(stderr, "process %d exits with %d\n", pid, status);
}
/* If there are no pipes, we do not need to run as many forks. Instead we will simply
* trim the args list, and operate the commands one by one. */
else {
pid = fork();
if (pid == 0) { /* child process */
for (int i = 0; i < nargs; i++) {
/* If we find the output operator, we know that the next item in the list
* will be name of the output file to be created/overwritten. */
if (!strcmp(args[i], ">")) {
//This stores the name into output.
//Creat ensures that the file is overwritten.
Output = creat(args[i + 1], 0640);
}
/* If we find the input operator, we know that the next item in the list
* will be name of the input file to be used as an input. */
if (!strcmp(args[i], "<")) {
//Stores name into input and makes it read only.
Input = open(args[i + 1], O_RDONLY, 0666);
}
}
/* Next we run a for loop to populate sub_args with a sublist of each command.
* In this particular loop, we're simply looking to keep track of the unix commands called
* before the output redirection.
*
* These commands are then stored, and the null character is added at the end. Finally,
* we run the operation using execvp. */
for (int i = 0; i < nargs; i++) {
if (!strcmp(args[i], ">")) {
//The simple loop to populate sub_arg.
pop_arg(args, sub_args, i);
/* Here we simply change the stream from stdin, to out input file.
* Then we also change our stream from stdout to our output file.
* */
if(Input){
dup2(Input, fileno(stdin));
close(Input);
}
if(Output){
dup2(Output, fileno(stdout));
close(Output);
}
execvp(sub_args[0], sub_args);
}
/* The operations here function virtually in the same way.
* However, instead, we call open so that we can append to a file, rather than overwrite. */
if (!strcmp(args[i], ">>")) {
pop_arg(args, sub_args, i);
//fileID is equal to the name of file, but can be appended to.
fileId = open(args[i + 1], O_RDWR | O_APPEND, 0666);
//Finally we change the stream
dup2(fileId, fileno(stdout));
close(fileId);
//Command outputs to the file.
execvp(sub_args[0], sub_args);
}
/* Same sequence as above. This time we change the stdin to our file.*/
if (!strcmp(args[i], "<")) {
pop_arg(args, sub_args, i);
if(Input){
dup2(Input, fileno(stdin));
close(Input);
}
if(Output){
dup2(Output, fileno(stdout));
close(Output);
}
execvp(sub_args[0], sub_args);
//Run command.
execvp(sub_args[0], sub_args);
}
}
//If all the above isn't necessary, the command can still run as intended.
execvp(args[0], args);
perror("exec failed");
exit(-1);
}
else if (pid > 0) { /* parent process */
if (!async) waitpid(pid, NULL, 0);
else printf("this is an async call\n");
}
else { /* error occurred */
perror("fork failed");
exit(1);
}
}
}
int main (int argc, char* argv [])
{
char cmdline[BUFSIZE];
for(;;) {
printf("COP4338$ ");
if(fgets(cmdline, BUFSIZE, stdin) == NULL) {
perror("fgets failed");
exit(1);
}
execute(cmdline) ;
}
return 0;
}