-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmush.c
235 lines (207 loc) · 7.31 KB
/
mush.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
/* mush.c
* a Minimally Usable SHell
* edited by Tina Zhao
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAXNEST 8
extern char **environ; /* the environment */
char command[100]; /* the command line */
char * argv[100]; /* result of parsing the command line */
int argc; /* the number of arguments */
bool errors; /* were there any errors in it */
char * path; /* the search path extracted from the environment */
char * patha[100]; /* result of parsing the path */
bool seekable; /* is this file seekable? */
int loopnest; /* nesting level of loops */
long int looptop[ MAXNEST ]; /* file positions of loop tops */
void getcommand() {
char ch;
int i = 0;
putchar( '>' );
do {
ch = getchar();
command[i] = ch;
i++;
} while (ch != '\n');
command[i - 1] = '\000';
}
void parseargv() {
int i = 0;
int j = 0;
for (;;) {
while (command[j] == ' ') j++;
if (command[j] == '\000') break;
argv[i] = &command[j];
i++;
while ((command[j] != ' ')
&& (command[j] != '\000')) {
j++;
}
if (command[j] == '\000') break;
command[j] = '\000';
j++;
}
argc = i - 1;
argv[i] = NULL;
}
void dollarsigns() {
/* process shell variable substitutions */
int i = 0; /* loop index */
while (argv[i] != NULL) {
if (argv[i][0] == '$') { /* ref to a shell variable */
char * value; /* value of the variable */
value = getenv( &argv[i][1] );
if (value != NULL) { /* value is defined */
argv[i] = value;
} else { /* value is not defined */
printf( "no such variable: %s\n",
argv[i] );
errors = true;
}
}
i++;
}
}
void getparsepath() {
/* get the path and pick it apart into patha */
int i = 0; /* index into patha, array of path part pointers */
int j = 0; /* index into path, the text of the value of $PATH */
/* get a pointer to $PATH in the environ */
path = getenv( "PATH" );
if (path != NULL) { /* $PATH might not be defined in environ */
/* make a copy of path so we can still pass environ */
path = strcpy( malloc( strlen( path ) ), path );
/* pick out components of path, index them in patha */
/* this logic was borrowed from parseargv */
for (;;) {
if (path[j] == '\000') break;
patha[i] = &path[j];
i++;
while ((path[j] != ':')
&& (path[j] != '\000')) {
j++;
}
if (path[j] == '\000') break;
path[j] = '\000';
j++;
}
}
patha[i] = NULL;
}
char * gluepath( char * left, char * right ) {
/* concatenate left and right, with a / between them */
char * result;
/* how long is result string? including / and null terminator */
result = malloc( strlen( left ) + 1 + strlen( right ) + 1 );
strcpy( result, left );
strcat( result, "/" );
strcat( result, right );
return result;
}
void launch() {
/* we flush here to keep output in order */
fflush( stdout );
if (fork() == 0) { /*child*/
int i;
/* first try it with the literal command */
execve( argv[0], argv, environ );
/* if that fails, try with successive path components */
getparsepath();
i = 0;
while (patha[i] != NULL) {
execve( gluepath( patha[i], argv[0] ),
argv, environ );
i++;
}
/* if that fails, we are lost */
printf( "no such command\n" );
exit( EXIT_FAILURE );
} else { /*parent*/
wait( NULL );
}
}
void doexit() {
exit( EXIT_SUCCESS );
}
void dosetenv() {
if (argc < 1) {
printf( "setenv with missing argument\n" );
} else if (argc == 1) {
/* setenv without a specified value */
int ret = setenv( argv[1], "", 1 );
if (ret != 0) {
printf( "bad variable name\n" );
}
} else if (argc == 2) {
/* the normal setenv with a value specified */
int ret = setenv( argv[1], argv[2], 1 );
if (ret != 0) {
printf( "bad variable name\n" );
}
} else { /* argc > 2 */
printf( "setenv with extra arguments\n" );
}
}
void doloop() {
if (argc > 0) {
printf( "loop with extra arguments\n" );
}
loopnest = loopnest + 1;
if (loopnest >= MAXNEST) {
printf( "loop with too much nesting\n" );
} else {
looptop[ loopnest ] = ftell( stdin );
}
}
void dowhile() {
if (loopnest >= MAXNEST) {
printf( "while with too much nesting\n" );
loopnest = loopnest - 1;
} else if (loopnest < 0) {
printf( "while with no matching loop command\n" );
} else if (!seekable) {
printf( "while when non-seekable input file\n" );
loopnest = loopnest - 1;
} else if (argc > 1) {
printf( "while with too many arguments\n" );
loopnest = loopnest - 1;
} else if ((argc == 1) && (argv[1][0] != '\000')) {
/* no error and argument interpreted as true */
fseek( stdin, looptop[ loopnest ], SEEK_SET );
/* we stay in loop -- don't decrement loopnest */
} else {
/* no error and argument interpreted as false */
loopnest = loopnest - 1;
}
}
void docommand() {
/* process built-in commands or launch an application */
if ( argv[0] == NULL ) {
/* prevent segmentation fault on blank lines */
} else if (!strcmp( argv[0], "exit" )) {
doexit();
} else if (!strcmp( argv[0], "setenv" )) {
dosetenv();
} else if (!strcmp( argv[0], "loop" )) {
doloop();
} else if (!strcmp( argv[0], "while" )) {
dowhile();
} else { /* not a built-in command */
launch();
}
}
int main() {
/* initialization for loop processing */
seekable = (ftell( stdin ) >= 0);
loopnest = -1;
for (;;) {
errors = false;
getcommand();
parseargv();
dollarsigns();
if (!errors) docommand();
}
}