* same with xv6
[mascara-docs.git] / i386 / ucla / src / lab5 / user / sh.c
blobb8fe373471a8755e7c1fff3228408bc501f308ae
1 #include <inc/lib.h>
3 #define BUFSIZ 1024 /* Find the buffer overrun bug! */
4 int debug = 0;
7 // gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
8 // gettoken(0, token) parses a shell token from the previously set string,
9 // null-terminates that token, stores the token pointer in '*token',
10 // and returns a token ID (0, '<', '>', '|', or 'w').
11 // Subsequent calls to 'gettoken(0, token)' will return subsequent
12 // tokens from the string.
13 int gettoken(char *s, char **token);
16 // Parse a shell command from string 's' and execute it.
17 // Do not return until the shell command is finished.
18 // runcmd() is called in a forked child,
19 // so it's OK to manipulate file descriptor state.
20 #define MAXARGS 16
21 void
22 runcmd(char* s)
24 char *argv[MAXARGS], *t, argv0buf[BUFSIZ];
25 int argc, c, i, r, p[2], fd, pipe_child;
27 pipe_child = 0;
28 gettoken(s, 0);
30 again:
31 argc = 0;
32 while (1) {
33 switch ((c = gettoken(0, &t))) {
35 case 'w': // Add an argument
36 if (argc == MAXARGS) {
37 cprintf("too many arguments\n");
38 exit();
40 argv[argc++] = t;
41 break;
43 case '<': // Input redirection
44 // Grab the filename from the argument list
45 if (gettoken(0, &t) != 'w') {
46 cprintf("syntax error: < not followed by word\n");
47 exit();
49 // Open 't' for reading as file descriptor 0
50 // (which environments use as standard input).
51 // We can't open a file onto a particular descriptor,
52 // so open the file as 'fd',
53 // then check whether 'fd' is 0.
54 // If not, dup 'fd' onto file descriptor 0,
55 // then close the original 'fd'.
57 // LAB 5: Your code here.
58 panic("< redirection not implemented");
59 break;
61 case '>': // Output redirection
62 // Grab the filename from the argument list
63 if (gettoken(0, &t) != 'w') {
64 cprintf("syntax error: > not followed by word\n");
65 exit();
67 // Open 't' for writing as file descriptor 1
68 // (which environments use as standard output).
69 // We can't open a file onto a particular descriptor,
70 // so open the file as 'fd',
71 // then check whether 'fd' is 1.
72 // If not, dup 'fd' onto file descriptor 1,
73 // then close the original 'fd'.
74 // Also, truncate fd.
76 // LAB 5: Your code here.
77 panic("> redirection not implemented");
78 break;
80 case '|': // Pipe
81 // Set up pipe redirection.
83 // Allocate a pipe by calling 'pipe(p)'.
84 // Like the Unix version of pipe() (man 2 pipe),
85 // this function allocates two file descriptors;
86 // data written onto 'p[1]' can be read from 'p[0]'.
87 // Then fork.
88 // The child runs the right side of the pipe:
89 // Use dup() to duplicate the read end of the pipe
90 // (p[0]) onto file descriptor 0 (standard input).
91 // Then close the pipe (both p[0] and p[1]).
92 // (The read end will still be open, as file
93 // descriptor 0.)
94 // Then 'goto again', to parse the rest of the
95 // command line as a new command.
96 // The parent runs the left side of the pipe:
97 // Set 'pipe_child' to the child env ID.
98 // dup() the write end of the pipe onto
99 // file descriptor 1 (standard output).
100 // Then close the pipe.
101 // Then 'goto runit', to execute this piece of
102 // the pipeline.
104 // LAB 5: Your code here.
105 panic("| not implemented");
106 break;
108 case 0: // String is complete
109 // Run the current command!
110 goto runit;
112 default:
113 panic("bad return %d from gettoken", c);
114 break;
119 runit:
120 // Return immediately if command line was empty.
121 if(argc == 0) {
122 if (debug)
123 cprintf("EMPTY COMMAND\n");
124 return;
127 // Clean up command line.
128 // Read all commands from the filesystem: add an initial '/' to
129 // the command name.
130 // This essentially acts like 'PATH=/'.
131 if (argv[0][0] != '/') {
132 argv0buf[0] = '/';
133 strcpy(argv0buf + 1, argv[0]);
134 argv[0] = argv0buf;
136 argv[argc] = 0;
138 // Print the command.
139 if (debug) {
140 cprintf("[%08x] SPAWN:", thisenv->env_id);
141 for (i = 0; argv[i]; i++)
142 cprintf(" %s", argv[i]);
143 cprintf("\n");
146 // Spawn the command!
147 if ((r = spawn(argv[0], (const char**) argv)) < 0)
148 cprintf("spawn %s: %e\n", argv[0], r);
150 // In the parent of the spawned command, close all file descriptors
151 // and wait for the spawned command to exit.
152 close_all();
153 if (r >= 0) {
154 if (debug)
155 cprintf("[%08x] WAIT %s %08x\n", thisenv->env_id, argv[0], r);
156 wait(r);
157 if (debug)
158 cprintf("[%08x] wait finished\n", thisenv->env_id);
161 // If we were the left-hand part of a pipe,
162 // wait for the right-hand part to finish.
163 if (pipe_child) {
164 if (debug)
165 cprintf("[%08x] WAIT pipe_child %08x\n", thisenv->env_id, pipe_child);
166 wait(pipe_child);
167 if (debug)
168 cprintf("[%08x] wait finished\n", thisenv->env_id);
171 // Done!
172 exit();
176 // Get the next token from string s.
177 // Set *p1 to the beginning of the token and *p2 just past the token.
178 // Returns
179 // 0 for end-of-string;
180 // < for <;
181 // > for >;
182 // | for |;
183 // w for a word.
185 // Eventually (once we parse the space where the \0 will go),
186 // words get nul-terminated.
187 #define WHITESPACE " \t\r\n"
188 #define SYMBOLS "<|>&;()"
191 _gettoken(char *s, char **p1, char **p2)
193 int t;
195 if (s == 0) {
196 if (debug > 1)
197 cprintf("GETTOKEN NULL\n");
198 return 0;
201 if (debug > 1)
202 cprintf("GETTOKEN: %s\n", s);
204 *p1 = 0;
205 *p2 = 0;
207 while (strchr(WHITESPACE, *s))
208 *s++ = 0;
209 if (*s == 0) {
210 if (debug > 1)
211 cprintf("EOL\n");
212 return 0;
214 if (strchr(SYMBOLS, *s)) {
215 t = *s;
216 *p1 = s;
217 *s++ = 0;
218 *p2 = s;
219 if (debug > 1)
220 cprintf("TOK %c\n", t);
221 return t;
223 *p1 = s;
224 while (*s && !strchr(WHITESPACE SYMBOLS, *s))
225 s++;
226 *p2 = s;
227 if (debug > 1) {
228 t = **p2;
229 **p2 = 0;
230 cprintf("WORD: %s\n", *p1);
231 **p2 = t;
233 return 'w';
237 gettoken(char *s, char **p1)
239 static int c, nc;
240 static char* np1, *np2;
242 if (s) {
243 nc = _gettoken(s, &np1, &np2);
244 return 0;
246 c = nc;
247 *p1 = np1;
248 nc = _gettoken(np2, &np1, &np2);
249 return c;
253 void
254 usage(void)
256 cprintf("usage: sh [-dix] [command-file]\n");
257 exit();
260 asmlinkage void
261 umain(int argc, char **argv)
263 int r, interactive, echocmds;
264 struct Argstate args;
265 cprintf("[%08x] sh\n", thisenv->env_id);
267 interactive = '?';
268 echocmds = 0;
269 argstart(&argc, argv, &args);
270 while ((r = argnext(&args)) >= 0)
271 switch (r) {
272 case 'd':
273 debug++;
274 break;
275 case 'i':
276 interactive = 1;
277 break;
278 case 'x':
279 echocmds = 1;
280 break;
281 default:
282 usage();
285 if (argc > 2)
286 usage();
287 if (argc == 2) {
288 close(0);
289 if ((r = open(argv[1], O_RDONLY)) < 0)
290 panic("open %s: %e", argv[1], r);
291 assert(r == 0);
293 if (interactive == '?') {
294 interactive = iscons(0);
295 assert(interactive >= 0);
298 while (1) {
299 char *buf;
301 buf = readline(interactive ? "$ " : NULL);
302 if (buf == NULL) {
303 if (debug)
304 cprintf("EXITING\n");
305 exit(); // end of file
307 if (debug)
308 cprintf("LINE: %s\n", buf);
309 if (buf[0] == '#')
310 continue;
311 if (echocmds)
312 fprintf(1, "# %s\n", buf);
313 if (debug)
314 cprintf("BEFORE FORK\n");
315 if ((r = fork()) < 0)
316 panic("fork: %e", r);
317 if (debug)
318 cprintf("FORK: %d\n", r);
319 if (r == 0) {
320 runcmd(buf);
321 exit();
322 } else
323 wait(r);