-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmyshell.c
147 lines (129 loc) · 3.61 KB
/
myshell.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
/* CS 347 -- Mini Shell!
* Original author Phil Nelson 2000
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include "argparse.h"
#include "builtin.h"
/* PROTOTYPES */
void processline(char *line);
ssize_t getinput(char **line, size_t *size);
/*
* main
*
* Main entry point of the myshell program.
* This is essentially the primary read-eval-print loop of the command interpreter.
*
* Runs the shell in an endless loop until an exit command is issued.
*
* Hint: Use getinput and processline as appropriate.
*/
int main()
{
char *line = NULL;
size_t size = 0;
while (1)
{
printf("%% ");
ssize_t len = getinput(&line, &size); // Get input from user
if (len == -1)
{
break; // Exit on EOF
}
line[len - 1] = '\0'; // Remove newline character
if (strcmp(line, "exit") == 0)
{
break; // Exit on "exit" command
}
processline(line); // Process input
}
free(line); // Free the dynamically allocated buffer
return EXIT_SUCCESS;
}
/*
* getinput
*
* Prompts the user for a line of input (e.g. %myshell%) and stores it in a dynamically
* allocated buffer (pointed to by *line).
* If input fits in the buffer, it is stored in *line.
* If the buffer is too small, *line is freed and a larger buffer is allocated.
* The size of the buffer is stored in *size.
*
* Args:
* line: pointer to a char* that will be set to the address of the input buffer
* size: pointer to a size_t that will be set to the size of the buffer *line or 0 if *line is NULL.
*
* Returns:
* The length of the the string stored in *line.
*
* Hint: There is a standard i/o function that can make getinput easier than it sounds.
*/
ssize_t getinput(char **line, size_t *size)
{
ssize_t len = getline(line, size, stdin); // Read input from stdin
if (len == -1)
{
perror("getline"); // Handle EOF or input error
}
return len;
}
/*
* processline
*
* Interprets the input line as a command and either executes it as a built-in
* or forks a child process to execute an external program.
* Built-in commands are executed immediately.
* External commands are parsed then forked to be executed.
*
* Args:
* line: string containing a shell command and arguments
*
* Note: There are three cases to consider when forking a child process:
* 1. Fork fails
* 2. Fork succeeds and this is the child process
* 3. Fork succeeds and this is the parent process
*
* Hint: See the man page for fork(2) for more information.
* Hint: The process should only fork when the line is not empty and not trying to
* run a built-in command.
*/
void processline(char *line)
{
if (line == NULL || strlen(line) == 0)
return; // Ignore empty input
int arg_count;
char **arguments = argparse(line, &arg_count); // Parse input into arguments
if (arguments == NULL)
{
fprintf(stderr, "Failed to parse arguments.\n");
return;
}
// Check if it's a built-in command
if (!builtIn(arguments, arg_count))
{
pid_t cpid = fork(); // Fork a new process
if (cpid < 0) // Check if fork failed
{
perror("fork"); // Fork failed
}
else if (cpid == 0)
{ // Child process
execvp(arguments[0], arguments); // Execute external command
perror("execvp"); // If execvp fails
exit(EXIT_FAILURE);
}
else
{ // Parent process
int status;
waitpid(cpid, &status, 0); // Wait for child to finish
}
}
free(arguments); // Clean up allocated memory
}