-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
331 lines (286 loc) · 9.62 KB
/
main.c
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// ACADEMIC INTEGRITY PLEDGE
//
// - I have not used source code obtained from another student nor
// any other unauthorized source, either modified or unmodified.
//
// - All source code and documentation used in my program is either
// my original work or was derived by me from the source code
// published in the textbook for this course or presented in
// class.
//
// - I have not discussed coding details about this project with
// anyone other than my instructor. I understand that I may discuss
// the concepts of this program with other students and that another
// student may help me debug my program so long as neither of us
// writes anything during the discussion or modifies any computer
// file during the discussion.
//
// - I have violated neither the spirit nor letter of these restrictions.
//
//
//
// Signed:Mark P. Earl Date:10/17/2023
// 3460:426 Lab 1 - Basic C shell rev. 9/10/2020
/* Basic shell */
/*
* This is a very minimal shell. It finds an executable in the
* PATH, then loads it and executes it (using execv). Since
* it uses "." (dot) as a separator, it cannot handle file
* names like "minishell.h"
*
* The focus on this exercise is to use fork, PATH variables,
* and execv.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/wait.h>
#define MAX_ARGS 64
#define MAX_ARG_LEN 16
#define MAX_LINE_LEN 80
#define WHITESPACE " ,\t\n"
struct command_t
{
char *name;
int argc;
char *argv[MAX_ARGS];
};
/* Function prototypes */
int parseCommand(char *, struct command_t *);
void printPrompt();
void readCommand(char *);
int main(int argc, char *argv[])
{
int pid;
int status;
char cmdLine[MAX_LINE_LEN];
struct command_t command;
bool done = false;
while (!done)
{
printPrompt();
/* Read the command line and parse it */
readCommand(cmdLine);
parseCommand(cmdLine, &command);
command.argv[command.argc] = NULL;
// If you are not passing any command line arguments, you should define an empty array of strings and pass
// that as args.
if (command.argc == 1)
{
char *args[] = {NULL};
for (int i = 0; i < MAX_ARGS; ++i)
{
command.argv[i] = args[i];
}
}
/*
This shell directly supports the following internal commands:
• C file1 file2 Copy; create file2, copy all bytes of file1 to file2 without deleting file1.
• D file Delete the named file.
• E comment Echo; display comment on screen followed by a new line (multiple
spaces/tabs may be reduced to a single space); if no argument simply
issue a new prompt.
• H Help; display the user manual.
• L List the contents of the current directory.
• M file Make; create the named text file by launching a text editor.
• P file Print; display the contents of the named file on screen.
• Q Quit the shell.
• S Surf the web by launching a browser as a background process.
• W Wipe; clear the screen.
• X program Execute the named program.
All commands are case sensitive. Any command not part of this list should just be passed to execvp() and
normal execution attempted.
*/
switch (*command.name)
{
// C file1 file2 Copy; create file2, copy all bytes of file1 to file2 without deleting file1.
case 'C':
strcpy(command.name, "cp");
break;
// D file Delete the named file.
case 'D':
strcpy(command.name, "rm");
break;
// E comment Echo; display comment on screen followed by a new line (multiple
// spaces/tabs may be reduced to a single space); if no argument simply
// issue a new prompt.
case 'E':
strcpy(command.name, "echo");
break;
// H Help; display the user manual
case 'H':
strcpy(command.name, "man");
command.argv[0] = (char *)"man";
command.argv[1] = (char *)"./manpage.1";
command.argv[2] = NULL;
break;
// L List the contents of the current directory.
case 'L':
{
printf("\n");
// Create a child process for 'pwd'
int pid_pwd = fork();
if (pid_pwd == 0)
{
// Child process for 'pwd'
strcpy(command.name, "pwd");
status = execvp(command.name, command.argv);
if (status == -1)
{
perror("Error");
exit(EXIT_FAILURE);
}
}
else if (pid_pwd < 0)
{
perror("Fork error");
}
else
{
// Parent process
wait(NULL); // Wait for the 'pwd' command to finish
}
printf("\n");
// Create another child process for 'ls -l'
int pid_ls = fork();
if (pid_ls == 0)
{
// Child process for 'ls -l'
strcpy(command.name, "ls");
command.argv[0] = (char *)"ls";
command.argv[1] = (char *)"-l";
command.argv[2] = NULL;
status = execvp(command.name, command.argv);
if (status == -1)
{
perror("Error");
exit(EXIT_FAILURE);
}
}
else if (pid_ls < 0)
{
perror("Fork error");
}
else
{
// Parent process
wait(NULL); // Wait for the 'ls -l' command to finish
}
continue;
}
// M file Make; create the named text file by launching a text editor.
case 'M':
strcpy(command.name, "nano");
break;
// P file Print; display the contents of the named file on screen.
case 'P':
strcpy(command.name, "more");
break;
// Q Quit the shell.
case 'Q':
done = true;
break;
// S Surf the web by launching a browser as a background process.
case 'S':
strcpy(command.name, "firefox");
break;
// W Wipe; clear the screen.
case 'W':
strcpy(command.name, "clear");
break;
// X program Execute the named program.
case 'X':
strcpy(command.name, command.argv[1]);
break;
// Any command not part of this list should just be passed to execvp() and
// normal execution attempted. So no default case needed
default:
break;
}
// If the user did not request to quit the shell, attempt execution
if (!done)
{
/* Create a child process to execute the command */
if ((pid = fork()) == 0)
{
/* Child executing command */
// Check if the command name is not empty and is valid
if (strlen(command.name) > 0)
{
status = execvp(command.name, command.argv);
if (status == -1)
{
perror("Error");
exit(EXIT_FAILURE); // Exit the child process with an error code
}
}
// If the command name is empty (just pressing 'Enter') or an unknown command, exit the child process with success
else
{
exit(EXIT_SUCCESS);
}
return 0;
}
}
/* Wait for the child to terminate, with one exception, allow Firefox to run in background */
if (strcmp(command.name, "firefox") != 0)
{
wait(NULL);
}
}
/* Shell termination */
printf("\n\n shell: Terminating successfully\n");
return 0;
}
/* End basic shell */
/* Parse Command function */
/* Determine command name and construct the parameter list.
* This function will build argv[] and set the argc value.
* argc is the number of "tokens" or words on the command line
* argv[] is an array of strings (pointers to char *). The last
* element in argv[] must be NULL. As we scan the command line
* from the left, the first token goes in argv[0], the second in
* argv[1], and so on. Each time we add a token to argv[],
* we increment argc.
*/
int parseCommand(char *cLine, struct command_t *cmd)
{
int argc;
char **clPtr;
/* Initialization */
clPtr = &cLine; /* cLine is the command line */
argc = 0;
cmd->argv[argc] = (char *)malloc(MAX_ARG_LEN);
/* Fill argv[] */
while ((cmd->argv[argc] = strsep(clPtr, WHITESPACE)) != NULL)
{
cmd->argv[++argc] = (char *)malloc(MAX_ARG_LEN);
}
/* Set the command name and argc */
cmd->argc = argc - 1;
cmd->name = (char *)malloc(sizeof(cmd->argv[0]));
strcpy(cmd->name, cmd->argv[0]);
return 1;
}
/* End parseCommand function */
/* Print prompt and read command functions - Nutt pp. 79-80 */
void printPrompt()
{
/* Build the prompt string to have the machine name,
* current directory, or other desired information
*/
char promptString[] = "linux(mpe12)|>";
printf("%s ", promptString);
}
void readCommand(char *buffer)
{
/* This code uses any set of I/O functions, such as those in
* the stdio library to read the entire command line into
* the buffer. This implementation is greatly simplified,
* but it does the job.
*/
fgets(buffer, 80, stdin);
}
/* End printPrompt and readCommand */