diff options
Diffstat (limited to 'sh.c')
-rw-r--r-- | sh.c | 1255 |
1 files changed, 649 insertions, 606 deletions
@@ -39,293 +39,306 @@ | |||
39 | #include <unistd.h> | 39 | #include <unistd.h> |
40 | 40 | ||
41 | 41 | ||
42 | #define MAX_COMMAND_LEN 250 /* max length of a single command | 42 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
43 | string */ | 43 | #include "cmdedit.h" |
44 | #endif | ||
45 | |||
44 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 46 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
45 | 47 | ||
46 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND }; | 48 | |
49 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, | ||
50 | REDIRECT_APPEND }; | ||
47 | 51 | ||
48 | struct jobSet { | 52 | struct jobSet { |
49 | struct job * head; /* head of list of running jobs */ | 53 | struct job *head; /* head of list of running jobs */ |
50 | struct job * fg; /* current foreground job */ | 54 | struct job *fg; /* current foreground job */ |
51 | }; | 55 | }; |
52 | 56 | ||
53 | struct redirectionSpecifier { | 57 | struct redirectionSpecifier { |
54 | enum redirectionType type; /* type of redirection */ | 58 | enum redirectionType type; /* type of redirection */ |
55 | int fd; /* file descriptor being redirected */ | 59 | int fd; /* file descriptor being redirected */ |
56 | char * filename; /* file to redirect fd to */ | 60 | char *filename; /* file to redirect fd to */ |
57 | }; | 61 | }; |
58 | 62 | ||
59 | struct childProgram { | 63 | struct childProgram { |
60 | pid_t pid; /* 0 if exited */ | 64 | pid_t pid; /* 0 if exited */ |
61 | char ** argv; /* program name and arguments */ | 65 | char **argv; /* program name and arguments */ |
62 | int numRedirections; /* elements in redirection array */ | 66 | int numRedirections; /* elements in redirection array */ |
63 | struct redirectionSpecifier * redirections; /* I/O redirections */ | 67 | struct redirectionSpecifier *redirections; /* I/O redirections */ |
64 | glob_t globResult; /* result of parameter globbing */ | 68 | glob_t globResult; /* result of parameter globbing */ |
65 | int freeGlob; /* should we globfree(&globResult)? */ | 69 | int freeGlob; /* should we globfree(&globResult)? */ |
66 | int isStopped; /* is the program currently running? */ | 70 | int isStopped; /* is the program currently running? */ |
67 | }; | 71 | }; |
68 | 72 | ||
69 | struct job { | 73 | struct job { |
70 | int jobId; /* job number */ | 74 | int jobId; /* job number */ |
71 | int numProgs; /* total number of programs in job */ | 75 | int numProgs; /* total number of programs in job */ |
72 | int runningProgs; /* number of programs running */ | 76 | int runningProgs; /* number of programs running */ |
73 | char * text; /* name of job */ | 77 | char *text; /* name of job */ |
74 | char * cmdBuf; /* buffer various argv's point into */ | 78 | char *cmdBuf; /* buffer various argv's point into */ |
75 | pid_t pgrp; /* process group ID for the job */ | 79 | pid_t pgrp; /* process group ID for the job */ |
76 | struct childProgram * progs; /* array of programs in job */ | 80 | struct childProgram *progs; /* array of programs in job */ |
77 | struct job * next; /* to track background commands */ | 81 | struct job *next; /* to track background commands */ |
78 | int stoppedProgs; /* number of programs alive, but stopped */ | 82 | int stoppedProgs; /* number of programs alive, but stopped */ |
79 | }; | 83 | }; |
80 | 84 | ||
81 | struct builtInCommand { | 85 | struct builtInCommand { |
82 | char *cmd; /* name */ | 86 | char *cmd; /* name */ |
83 | char *descr; /* description */ | 87 | char *descr; /* description */ |
84 | char *usage; /* usage */ | 88 | char *usage; /* usage */ |
85 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ | 89 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ |
86 | }; | 90 | }; |
87 | 91 | ||
88 | /* Some function prototypes */ | 92 | /* Some function prototypes */ |
89 | static int shell_cd(struct job* cmd, struct jobSet* junk); | 93 | static int shell_cd(struct job *cmd, struct jobSet *junk); |
90 | static int shell_env(struct job* dummy, struct jobSet* junk); | 94 | static int shell_env(struct job *dummy, struct jobSet *junk); |
91 | static int shell_exit(struct job* cmd, struct jobSet* junk); | 95 | static int shell_exit(struct job *cmd, struct jobSet *junk); |
92 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList); | 96 | static int shell_fg_bg(struct job *cmd, struct jobSet *jobList); |
93 | static int shell_help(struct job* cmd, struct jobSet* junk); | 97 | static int shell_help(struct job *cmd, struct jobSet *junk); |
94 | static int shell_jobs(struct job* dummy, struct jobSet* jobList); | 98 | static int shell_jobs(struct job *dummy, struct jobSet *jobList); |
95 | static int shell_pwd(struct job* dummy, struct jobSet* junk); | 99 | static int shell_pwd(struct job *dummy, struct jobSet *junk); |
96 | static int shell_set(struct job* cmd, struct jobSet* junk); | 100 | static int shell_set(struct job *cmd, struct jobSet *junk); |
97 | static int shell_source(struct job* cmd, struct jobSet* jobList); | 101 | static int shell_source(struct job *cmd, struct jobSet *jobList); |
98 | static int shell_unset(struct job* cmd, struct jobSet* junk); | 102 | static int shell_unset(struct job *cmd, struct jobSet *junk); |
99 | 103 | ||
100 | static void checkJobs(struct jobSet * jobList); | 104 | static void checkJobs(struct jobSet *jobList); |
101 | static int getCommand(FILE * source, char * command); | 105 | static int getCommand(FILE * source, char *command); |
102 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg); | 106 | static int parseCommand(char **commandPtr, struct job *job, int *isBg); |
103 | static int setupRedirections(struct childProgram * prog); | 107 | static int setupRedirections(struct childProgram *prog); |
104 | static int runCommand(struct job newJob, struct jobSet * jobList, int inBg); | 108 | static int runCommand(struct job newJob, struct jobSet *jobList, int inBg); |
105 | static int busy_loop(FILE * input); | 109 | static int busy_loop(FILE * input); |
106 | 110 | ||
107 | 111 | ||
108 | /* Table of built-in functions */ | 112 | /* Table of built-in functions */ |
109 | static struct builtInCommand bltins[] = { | 113 | static struct builtInCommand bltins[] = { |
110 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, | 114 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, |
111 | {"cd", "Change working directory", "cd [dir]", shell_cd}, | 115 | {"cd", "Change working directory", "cd [dir]", shell_cd}, |
112 | {"env", "Print all environment variables", "env", shell_env}, | 116 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, |
113 | {"exit", "Exit from shell()", "exit", shell_exit}, | 117 | {"env", "Print all environment variables", "env", shell_env}, |
114 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, | 118 | {"exit", "Exit from shell()", "exit", shell_exit}, |
115 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, | 119 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, |
116 | {"pwd", "Print current directory", "pwd", shell_pwd}, | 120 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, |
117 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, | 121 | {"pwd", "Print current directory", "pwd", shell_pwd}, |
118 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | 122 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, |
119 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, | 123 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, |
120 | {".", "Source-in and run commands in a file", ". filename", shell_source}, | 124 | {".", "Source-in and run commands in a file", ". filename", |
121 | {"help", "List shell built-in commands", "help", shell_help}, | 125 | shell_source}, |
122 | {NULL, NULL, NULL, NULL} | 126 | {"help", "List shell built-in commands", "help", shell_help}, |
127 | {NULL, NULL, NULL, NULL} | ||
123 | }; | 128 | }; |
124 | 129 | ||
125 | static const char shell_usage[] = | 130 | static const char shell_usage[] = |
126 | "sh [FILE]...\n\n" | 131 | "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n"; |
127 | "The BusyBox command interpreter (shell).\n\n"; | ||
128 | 132 | ||
129 | 133 | ||
130 | static char cwd[1024]; | 134 | static char cwd[1024]; |
131 | static char *prompt = "# "; | 135 | static char *prompt = "# "; |
132 | 136 | ||
133 | 137 | ||
138 | |||
134 | /* built-in 'cd <path>' handler */ | 139 | /* built-in 'cd <path>' handler */ |
135 | static int shell_cd(struct job* cmd, struct jobSet* junk) | 140 | static int shell_cd(struct job *cmd, struct jobSet *junk) |
136 | { | 141 | { |
137 | char *newdir; | 142 | char *newdir; |
138 | if (!cmd->progs[0].argv[1] == 1) | 143 | if (!cmd->progs[0].argv[1] == 1) |
139 | newdir = getenv("HOME"); | 144 | newdir = getenv("HOME"); |
140 | else | 145 | else |
141 | newdir = cmd->progs[0].argv[1]; | 146 | newdir = cmd->progs[0].argv[1]; |
142 | if (chdir(newdir)) { | 147 | if (chdir(newdir)) { |
143 | printf("cd: %s: %s\n", newdir, strerror(errno)); | 148 | printf("cd: %s: %s\n", newdir, strerror(errno)); |
144 | return FALSE; | 149 | return FALSE; |
145 | } | 150 | } |
146 | getcwd(cwd, sizeof(cwd)); | 151 | getcwd(cwd, sizeof(cwd)); |
147 | 152 | ||
148 | return TRUE; | 153 | return TRUE; |
149 | } | 154 | } |
150 | 155 | ||
151 | /* built-in 'env' handler */ | 156 | /* built-in 'env' handler */ |
152 | static int shell_env(struct job* dummy, struct jobSet* junk) | 157 | static int shell_env(struct job *dummy, struct jobSet *junk) |
153 | { | 158 | { |
154 | char **e; | 159 | char **e; |
155 | 160 | ||
156 | for (e = environ ; *e ; e++) { | 161 | for (e = environ; *e; e++) { |
157 | fprintf(stdout, "%s\n", *e); | 162 | fprintf(stdout, "%s\n", *e); |
158 | } | 163 | } |
159 | return (0); | 164 | return (0); |
160 | } | 165 | } |
161 | 166 | ||
162 | /* built-in 'exit' handler */ | 167 | /* built-in 'exit' handler */ |
163 | static int shell_exit(struct job* cmd, struct jobSet* junk) | 168 | static int shell_exit(struct job *cmd, struct jobSet *junk) |
164 | { | 169 | { |
165 | if (!cmd->progs[0].argv[1] == 1) | 170 | if (!cmd->progs[0].argv[1] == 1) |
166 | exit TRUE; | 171 | exit TRUE; |
167 | else | 172 | else |
168 | exit(atoi(cmd->progs[0].argv[1])); | 173 | exit(atoi(cmd->progs[0].argv[1])); |
169 | } | 174 | } |
170 | 175 | ||
171 | /* built-in 'fg' and 'bg' handler */ | 176 | /* built-in 'fg' and 'bg' handler */ |
172 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList) | 177 | static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) |
173 | { | 178 | { |
174 | int i, jobNum; | 179 | int i, jobNum; |
175 | struct job* job; | 180 | struct job *job; |
176 | 181 | ||
177 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { | 182 | if (!jobList->head) { |
178 | fprintf(stderr, "%s: exactly one argument is expected\n", | 183 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { |
179 | cmd->progs[0].argv[0]); | 184 | fprintf(stderr, "%s: exactly one argument is expected\n", |
180 | return FALSE; | 185 | cmd->progs[0].argv[0]); |
181 | } | 186 | return FALSE; |
182 | 187 | } | |
183 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { | 188 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { |
184 | fprintf(stderr, "%s: bad argument '%s'\n", | 189 | fprintf(stderr, "%s: bad argument '%s'\n", |
185 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); | 190 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); |
186 | return FALSE; | 191 | return FALSE; |
187 | } | 192 | } |
188 | 193 | } else { | |
189 | for (job = jobList->head; job; job = job->next) | 194 | job = jobList->head; |
190 | if (job->jobId == jobNum) break; | 195 | } |
191 | 196 | ||
192 | if (!job) { | 197 | for (job = jobList->head; job; job = job->next) |
193 | fprintf(stderr, "%s: unknown job %d\n", | 198 | if (job->jobId == jobNum) |
194 | cmd->progs[0].argv[0], jobNum); | 199 | break; |
195 | return FALSE; | 200 | |
196 | } | 201 | if (!job) { |
197 | 202 | fprintf(stderr, "%s: unknown job %d\n", | |
198 | if (*cmd->progs[0].argv[0] == 'f') { | 203 | cmd->progs[0].argv[0], jobNum); |
199 | /* Make this job the foreground job */ | 204 | return FALSE; |
200 | 205 | } | |
201 | if (tcsetpgrp(0, job->pgrp)) | 206 | |
202 | perror("tcsetpgrp"); | 207 | if (*cmd->progs[0].argv[0] == 'f') { |
203 | jobList->fg = job; | 208 | /* Make this job the foreground job */ |
204 | } | 209 | |
205 | 210 | if (tcsetpgrp(0, job->pgrp)) | |
206 | /* Restart the processes in the job */ | 211 | perror("tcsetpgrp"); |
207 | for (i = 0; i < job->numProgs; i++) | 212 | jobList->fg = job; |
208 | job->progs[i].isStopped = 0; | 213 | } |
209 | 214 | ||
210 | kill(-job->pgrp, SIGCONT); | 215 | /* Restart the processes in the job */ |
211 | 216 | for (i = 0; i < job->numProgs; i++) | |
212 | job->stoppedProgs = 0; | 217 | job->progs[i].isStopped = 0; |
213 | 218 | ||
214 | return TRUE; | 219 | kill(-job->pgrp, SIGCONT); |
220 | |||
221 | job->stoppedProgs = 0; | ||
222 | |||
223 | return TRUE; | ||
215 | } | 224 | } |
216 | 225 | ||
217 | /* built-in 'help' handler */ | 226 | /* built-in 'help' handler */ |
218 | static int shell_help(struct job* cmd, struct jobSet* junk) | 227 | static int shell_help(struct job *cmd, struct jobSet *junk) |
219 | { | 228 | { |
220 | struct builtInCommand *x; | 229 | struct builtInCommand *x; |
221 | 230 | ||
222 | fprintf(stdout, "\nBuilt-in commands:\n"); | 231 | fprintf(stdout, "\nBuilt-in commands:\n"); |
223 | fprintf(stdout, "-------------------\n"); | 232 | fprintf(stdout, "-------------------\n"); |
224 | for ( x=bltins; x->cmd; x++) { | 233 | for (x = bltins; x->cmd; x++) { |
225 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); | 234 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); |
226 | } | 235 | } |
227 | fprintf(stdout, "\n\n"); | 236 | fprintf(stdout, "\n\n"); |
228 | return TRUE; | 237 | return TRUE; |
229 | } | 238 | } |
230 | 239 | ||
231 | /* built-in 'jobs' handler */ | 240 | /* built-in 'jobs' handler */ |
232 | static int shell_jobs(struct job* dummy, struct jobSet* jobList) | 241 | static int shell_jobs(struct job *dummy, struct jobSet *jobList) |
233 | { | 242 | { |
234 | struct job * job; | 243 | struct job *job; |
235 | char * statusString; | 244 | char *statusString; |
236 | for (job = jobList->head; job; job = job->next) { | 245 | for (job = jobList->head; job; job = job->next) { |
237 | if (job->runningProgs == job->stoppedProgs) | 246 | if (job->runningProgs == job->stoppedProgs) |
238 | statusString = "Stopped"; | 247 | statusString = "Stopped"; |
239 | else | 248 | else |
240 | statusString = "Running"; | 249 | statusString = "Running"; |
241 | 250 | ||
242 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, | 251 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text); |
243 | job->text); | 252 | } |
244 | } | 253 | return TRUE; |
245 | return TRUE; | ||
246 | } | 254 | } |
247 | 255 | ||
248 | 256 | ||
249 | /* built-in 'pwd' handler */ | 257 | /* built-in 'pwd' handler */ |
250 | static int shell_pwd(struct job* dummy, struct jobSet* junk) | 258 | static int shell_pwd(struct job *dummy, struct jobSet *junk) |
251 | { | 259 | { |
252 | getcwd(cwd, sizeof(cwd)); | 260 | getcwd(cwd, sizeof(cwd)); |
253 | fprintf(stdout, "%s\n", cwd); | 261 | fprintf(stdout, "%s\n", cwd); |
254 | return TRUE; | 262 | return TRUE; |
255 | } | 263 | } |
256 | 264 | ||
257 | /* built-in 'set VAR=value' handler */ | 265 | /* built-in 'set VAR=value' handler */ |
258 | static int shell_set(struct job* cmd, struct jobSet* junk) | 266 | static int shell_set(struct job *cmd, struct jobSet *junk) |
259 | { | 267 | { |
260 | int res; | 268 | int res; |
261 | 269 | ||
262 | if (!cmd->progs[0].argv[1] == 1) { | 270 | if (!cmd->progs[0].argv[1] == 1) { |
263 | return (shell_env(cmd, junk)); | 271 | return (shell_env(cmd, junk)); |
264 | } | 272 | } |
265 | res = putenv(cmd->progs[0].argv[1]); | 273 | res = putenv(cmd->progs[0].argv[1]); |
266 | if (res) | 274 | if (res) |
267 | fprintf(stdout, "set: %s\n", strerror(errno)); | 275 | fprintf(stdout, "set: %s\n", strerror(errno)); |
268 | return (res); | 276 | return (res); |
269 | } | 277 | } |
270 | 278 | ||
271 | /* Built-in '.' handler (read-in and execute commands from file) */ | 279 | /* Built-in '.' handler (read-in and execute commands from file) */ |
272 | static int shell_source(struct job* cmd, struct jobSet* junk) | 280 | static int shell_source(struct job *cmd, struct jobSet *junk) |
273 | { | 281 | { |
274 | FILE *input; | 282 | FILE *input; |
275 | int status; | 283 | int status; |
276 | |||
277 | if (!cmd->progs[0].argv[1] == 1) | ||
278 | return FALSE; | ||
279 | |||
280 | input = fopen(cmd->progs[0].argv[1], "r"); | ||
281 | if (!input) { | ||
282 | fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]); | ||
283 | return FALSE; | ||
284 | } | ||
285 | 284 | ||
286 | /* Now run the file */ | 285 | if (!cmd->progs[0].argv[1] == 1) |
287 | status = busy_loop(input); | 286 | return FALSE; |
288 | return (status); | 287 | |
288 | input = fopen(cmd->progs[0].argv[1], "r"); | ||
289 | if (!input) { | ||
290 | fprintf(stdout, "Couldn't open file '%s'\n", | ||
291 | cmd->progs[0].argv[1]); | ||
292 | return FALSE; | ||
293 | } | ||
294 | |||
295 | /* Now run the file */ | ||
296 | status = busy_loop(input); | ||
297 | return (status); | ||
289 | } | 298 | } |
290 | 299 | ||
291 | /* built-in 'unset VAR' handler */ | 300 | /* built-in 'unset VAR' handler */ |
292 | static int shell_unset(struct job* cmd, struct jobSet* junk) | 301 | static int shell_unset(struct job *cmd, struct jobSet *junk) |
293 | { | 302 | { |
294 | if (!cmd->progs[0].argv[1] == 1) { | 303 | if (!cmd->progs[0].argv[1] == 1) { |
295 | fprintf(stdout, "unset: parameter required.\n"); | 304 | fprintf(stdout, "unset: parameter required.\n"); |
296 | return FALSE; | 305 | return FALSE; |
297 | } | 306 | } |
298 | unsetenv(cmd->progs[0].argv[1]); | 307 | unsetenv(cmd->progs[0].argv[1]); |
299 | return TRUE; | 308 | return TRUE; |
300 | } | 309 | } |
301 | 310 | ||
302 | /* free up all memory from a job */ | 311 | /* free up all memory from a job */ |
303 | static void freeJob(struct job * cmd) | 312 | static void freeJob(struct job *cmd) |
304 | { | 313 | { |
305 | int i; | 314 | int i; |
306 | 315 | ||
307 | for (i = 0; i < cmd->numProgs; i++) { | 316 | for (i = 0; i < cmd->numProgs; i++) { |
308 | free(cmd->progs[i].argv); | 317 | free(cmd->progs[i].argv); |
309 | if (cmd->progs[i].redirections) free(cmd->progs[i].redirections); | 318 | if (cmd->progs[i].redirections) |
310 | if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult); | 319 | free(cmd->progs[i].redirections); |
320 | if (cmd->progs[i].freeGlob) | ||
321 | globfree(&cmd->progs[i].globResult); | ||
311 | } | 322 | } |
312 | free(cmd->progs); | 323 | free(cmd->progs); |
313 | if (cmd->text) free(cmd->text); | 324 | if (cmd->text) |
325 | free(cmd->text); | ||
314 | free(cmd->cmdBuf); | 326 | free(cmd->cmdBuf); |
315 | } | 327 | } |
316 | 328 | ||
317 | /* remove a job from the jobList */ | 329 | /* remove a job from the jobList */ |
318 | static void removeJob(struct jobSet * jobList, struct job * job) | 330 | static void removeJob(struct jobSet *jobList, struct job *job) |
319 | { | 331 | { |
320 | struct job * prevJob; | 332 | struct job *prevJob; |
321 | 333 | ||
322 | freeJob(job); | 334 | freeJob(job); |
323 | if (job == jobList->head) { | 335 | if (job == jobList->head) { |
324 | jobList->head = job->next; | 336 | jobList->head = job->next; |
325 | } else { | 337 | } else { |
326 | prevJob = jobList->head; | 338 | prevJob = jobList->head; |
327 | while (prevJob->next != job) prevJob = prevJob->next; | 339 | while (prevJob->next != job) |
328 | prevJob->next = job->next; | 340 | prevJob = prevJob->next; |
341 | prevJob->next = job->next; | ||
329 | } | 342 | } |
330 | 343 | ||
331 | free(job); | 344 | free(job); |
@@ -333,56 +346,62 @@ static void removeJob(struct jobSet * jobList, struct job * job) | |||
333 | 346 | ||
334 | /* Checks to see if any background processes have exited -- if they | 347 | /* Checks to see if any background processes have exited -- if they |
335 | have, figure out why and see if a job has completed */ | 348 | have, figure out why and see if a job has completed */ |
336 | static void checkJobs(struct jobSet * jobList) | 349 | static void checkJobs(struct jobSet *jobList) |
337 | { | 350 | { |
338 | struct job * job; | 351 | struct job *job; |
339 | pid_t childpid; | 352 | pid_t childpid; |
340 | int status; | 353 | int status; |
341 | int progNum=0; | 354 | int progNum = 0; |
342 | 355 | ||
343 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | 356 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { |
344 | for (job = jobList->head; job; job = job->next) { | 357 | for (job = jobList->head; job; job = job->next) { |
345 | progNum = 0; | 358 | progNum = 0; |
346 | while (progNum < job->numProgs && | 359 | while (progNum < job->numProgs && |
347 | job->progs[progNum].pid != childpid) | 360 | job->progs[progNum].pid != childpid) progNum++; |
348 | progNum++; | 361 | if (progNum < job->numProgs) |
349 | if (progNum < job->numProgs) break; | 362 | break; |
350 | } | 363 | } |
351 | 364 | ||
352 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | 365 | if (WIFEXITED(status) || WIFSIGNALED(status)) { |
353 | /* child exited */ | 366 | /* child exited */ |
354 | job->runningProgs--; | 367 | job->runningProgs--; |
355 | job->progs[progNum].pid = 0; | 368 | job->progs[progNum].pid = 0; |
356 | 369 | ||
357 | if (!job->runningProgs) { | 370 | if (!job->runningProgs) { |
358 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); | 371 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); |
359 | removeJob(jobList, job); | 372 | removeJob(jobList, job); |
360 | } | 373 | } |
361 | } else { | 374 | } else { |
362 | /* child stopped */ | 375 | /* child stopped */ |
363 | job->stoppedProgs++; | 376 | job->stoppedProgs++; |
364 | job->progs[progNum].isStopped = 1; | 377 | job->progs[progNum].isStopped = 1; |
365 | 378 | ||
366 | if (job->stoppedProgs == job->numProgs) { | 379 | if (job->stoppedProgs == job->numProgs) { |
367 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text); | 380 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", |
368 | } | 381 | job->text); |
369 | } | 382 | } |
383 | } | ||
370 | } | 384 | } |
371 | 385 | ||
372 | if (childpid == -1 && errno != ECHILD) | 386 | if (childpid == -1 && errno != ECHILD) |
373 | perror("waitpid"); | 387 | perror("waitpid"); |
374 | } | 388 | } |
375 | 389 | ||
376 | static int getCommand(FILE * source, char * command) | 390 | static int getCommand(FILE * source, char *command) |
377 | { | 391 | { |
378 | if (source == stdin) { | 392 | if (source == stdin) { |
379 | fprintf(stdout, "%s %s", cwd, prompt); | 393 | fprintf(stdout, "BBSHELL %s %s", cwd, prompt); |
380 | fflush(stdout); | 394 | fflush(stdout); |
395 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
396 | cmdedit_read_input(fileno(stdin), fileno(stdout), command); | ||
397 | return 0; | ||
398 | #endif | ||
381 | } | 399 | } |
382 | 400 | ||
383 | if (!fgets(command, MAX_COMMAND_LEN, source)) { | 401 | if (!fgets(command, BUFSIZ - 2, source)) { |
384 | if (source == stdin) printf("\n"); | 402 | if (source == stdin) |
385 | return 1; | 403 | printf("\n"); |
404 | return 1; | ||
386 | } | 405 | } |
387 | 406 | ||
388 | /* remove trailing newline */ | 407 | /* remove trailing newline */ |
@@ -391,46 +410,48 @@ static int getCommand(FILE * source, char * command) | |||
391 | return 0; | 410 | return 0; |
392 | } | 411 | } |
393 | 412 | ||
394 | static void globLastArgument(struct childProgram * prog, int * argcPtr, | 413 | static void globLastArgument(struct childProgram *prog, int *argcPtr, |
395 | int * argcAllocedPtr) | 414 | int *argcAllocedPtr) |
396 | { | 415 | { |
397 | int argc = *argcPtr; | 416 | int argc = *argcPtr; |
398 | int argcAlloced = *argcAllocedPtr; | 417 | int argcAlloced = *argcAllocedPtr; |
399 | int rc; | 418 | int rc; |
400 | int flags; | 419 | int flags; |
401 | int i; | 420 | int i; |
402 | char * src, * dst; | 421 | char *src, *dst; |
403 | 422 | ||
404 | if (argc > 1) { /* cmd->globResult is already initialized */ | 423 | if (argc > 1) { /* cmd->globResult is already initialized */ |
405 | flags = GLOB_APPEND; | 424 | flags = GLOB_APPEND; |
406 | i = prog->globResult.gl_pathc; | 425 | i = prog->globResult.gl_pathc; |
407 | } else { | 426 | } else { |
408 | prog->freeGlob = 1; | 427 | prog->freeGlob = 1; |
409 | flags = 0; | 428 | flags = 0; |
410 | i = 0; | 429 | i = 0; |
411 | } | 430 | } |
412 | 431 | ||
413 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); | 432 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); |
414 | if (rc == GLOB_NOSPACE) { | 433 | if (rc == GLOB_NOSPACE) { |
415 | fprintf(stderr, "out of space during glob operation\n"); | 434 | fprintf(stderr, "out of space during glob operation\n"); |
416 | return; | 435 | return; |
417 | } else if (rc == GLOB_NOMATCH || | 436 | } else if (rc == GLOB_NOMATCH || |
418 | (!rc && (prog->globResult.gl_pathc - i) == 1 && | 437 | (!rc && (prog->globResult.gl_pathc - i) == 1 && |
419 | !strcmp(prog->argv[argc - 1], | 438 | !strcmp(prog->argv[argc - 1], |
420 | prog->globResult.gl_pathv[i]))) { | 439 | prog->globResult.gl_pathv[i]))) { |
421 | /* we need to remove whatever \ quoting is still present */ | 440 | /* we need to remove whatever \ quoting is still present */ |
422 | src = dst = prog->argv[argc - 1]; | 441 | src = dst = prog->argv[argc - 1]; |
423 | while (*src) { | 442 | while (*src) { |
424 | if (*src != '\\') *dst++ = *src; | 443 | if (*src != '\\') |
425 | src++; | 444 | *dst++ = *src; |
426 | } | 445 | src++; |
427 | *dst = '\0'; | 446 | } |
447 | *dst = '\0'; | ||
428 | } else if (!rc) { | 448 | } else if (!rc) { |
429 | argcAlloced += (prog->globResult.gl_pathc - i); | 449 | argcAlloced += (prog->globResult.gl_pathc - i); |
430 | prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); | 450 | prog->argv = |
431 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, | 451 | realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); |
432 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); | 452 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, |
433 | argc += (prog->globResult.gl_pathc - i - 1); | 453 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); |
454 | argc += (prog->globResult.gl_pathc - i - 1); | ||
434 | } | 455 | } |
435 | 456 | ||
436 | *argcAllocedPtr = argcAlloced; | 457 | *argcAllocedPtr = argcAlloced; |
@@ -442,27 +463,28 @@ static void globLastArgument(struct childProgram * prog, int * argcPtr, | |||
442 | the beginning of the next command (if the original command had more | 463 | the beginning of the next command (if the original command had more |
443 | then one job associated with it) or NULL if no more commands are | 464 | then one job associated with it) or NULL if no more commands are |
444 | present. */ | 465 | present. */ |
445 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg) | 466 | static int parseCommand(char **commandPtr, struct job *job, int *isBg) |
446 | { | 467 | { |
447 | char * command; | 468 | char *command; |
448 | char * returnCommand = NULL; | 469 | char *returnCommand = NULL; |
449 | char * src, * buf, * chptr; | 470 | char *src, *buf, *chptr; |
450 | int argc = 0; | 471 | int argc = 0; |
451 | int done = 0; | 472 | int done = 0; |
452 | int argvAlloced; | 473 | int argvAlloced; |
453 | int i; | 474 | int i; |
454 | char quote = '\0'; | 475 | char quote = '\0'; |
455 | int count; | 476 | int count; |
456 | struct childProgram * prog; | 477 | struct childProgram *prog; |
457 | 478 | ||
458 | /* skip leading white space */ | 479 | /* skip leading white space */ |
459 | while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++; | 480 | while (**commandPtr && isspace(**commandPtr)) |
460 | 481 | (*commandPtr)++; | |
461 | /* this handles empty lines or leading '#' characters */ | 482 | |
462 | if (!**commandPtr || (**commandPtr=='#')) { | 483 | /* this handles empty lines or leading '#' characters */ |
463 | job->numProgs = 0; | 484 | if (!**commandPtr || (**commandPtr == '#')) { |
464 | *commandPtr = NULL; | 485 | job->numProgs = 0; |
465 | return 0; | 486 | *commandPtr = NULL; |
487 | return 0; | ||
466 | } | 488 | } |
467 | 489 | ||
468 | *isBg = 0; | 490 | *isBg = 0; |
@@ -491,177 +513,185 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg) | |||
491 | buf = command; | 513 | buf = command; |
492 | src = *commandPtr; | 514 | src = *commandPtr; |
493 | while (*src && !done) { | 515 | while (*src && !done) { |
494 | if (quote == *src) { | 516 | if (quote == *src) { |
495 | quote = '\0'; | 517 | quote = '\0'; |
496 | } else if (quote) { | 518 | } else if (quote) { |
497 | if (*src == '\\') { | 519 | if (*src == '\\') { |
498 | src++; | 520 | src++; |
499 | if (!*src) { | 521 | if (!*src) { |
500 | fprintf(stderr, "character expected after \\\n"); | 522 | fprintf(stderr, "character expected after \\\n"); |
501 | freeJob(job); | 523 | freeJob(job); |
502 | return 1; | 524 | return 1; |
503 | } | 525 | } |
504 | 526 | ||
505 | /* in shell, "\'" should yield \' */ | 527 | /* in shell, "\'" should yield \' */ |
506 | if (*src != quote) *buf++ = '\\'; | 528 | if (*src != quote) |
507 | } else if (*src == '*' || *src == '?' || *src == '[' || | 529 | *buf++ = '\\'; |
508 | *src == ']') | 530 | } else if (*src == '*' || *src == '?' || *src == '[' || |
509 | *buf++ = '\\'; | 531 | *src == ']') *buf++ = '\\'; |
510 | *buf++ = *src; | 532 | *buf++ = *src; |
511 | } else if (isspace(*src)) { | 533 | } else if (isspace(*src)) { |
512 | if (*prog->argv[argc]) { | 534 | if (*prog->argv[argc]) { |
513 | buf++, argc++; | 535 | buf++, argc++; |
514 | /* +1 here leaves room for the NULL which ends argv */ | 536 | /* +1 here leaves room for the NULL which ends argv */ |
515 | if ((argc + 1) == argvAlloced) { | 537 | if ((argc + 1) == argvAlloced) { |
516 | argvAlloced += 5; | 538 | argvAlloced += 5; |
517 | prog->argv = realloc(prog->argv, | 539 | prog->argv = realloc(prog->argv, |
518 | sizeof(*prog->argv) * argvAlloced); | 540 | sizeof(*prog->argv) * |
519 | } | 541 | argvAlloced); |
520 | prog->argv[argc] = buf; | 542 | } |
521 | 543 | prog->argv[argc] = buf; | |
522 | globLastArgument(prog, &argc, &argvAlloced); | 544 | |
523 | } | 545 | globLastArgument(prog, &argc, &argvAlloced); |
524 | } else switch (*src) { | 546 | } |
525 | case '"': | 547 | } else |
526 | case '\'': | 548 | switch (*src) { |
527 | quote = *src; | 549 | case '"': |
528 | break; | 550 | case '\'': |
529 | 551 | quote = *src; | |
530 | case '#': /* comment */ | 552 | break; |
531 | done = 1; | 553 | |
532 | break; | 554 | case '#': /* comment */ |
533 | 555 | done = 1; | |
534 | case '>': /* redirections */ | 556 | break; |
535 | case '<': | 557 | |
536 | i = prog->numRedirections++; | 558 | case '>': /* redirections */ |
537 | prog->redirections = realloc(prog->redirections, | 559 | case '<': |
538 | sizeof(*prog->redirections) * (i + 1)); | 560 | i = prog->numRedirections++; |
539 | 561 | prog->redirections = realloc(prog->redirections, | |
540 | prog->redirections[i].fd = -1; | 562 | sizeof(*prog->redirections) * |
541 | if (buf != prog->argv[argc]) { | 563 | (i + 1)); |
542 | /* the stuff before this character may be the file number | 564 | |
543 | being redirected */ | 565 | prog->redirections[i].fd = -1; |
544 | prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10); | 566 | if (buf != prog->argv[argc]) { |
545 | 567 | /* the stuff before this character may be the file number | |
546 | if (*chptr && *prog->argv[argc]) { | 568 | being redirected */ |
547 | buf++, argc++; | 569 | prog->redirections[i].fd = |
548 | globLastArgument(prog, &argc, &argvAlloced); | 570 | strtol(prog->argv[argc], &chptr, 10); |
549 | } | 571 | |
550 | } | 572 | if (*chptr && *prog->argv[argc]) { |
551 | 573 | buf++, argc++; | |
552 | if (prog->redirections[i].fd == -1) { | 574 | globLastArgument(prog, &argc, &argvAlloced); |
553 | if (*src == '>') | 575 | } |
554 | prog->redirections[i].fd = 1; | 576 | } |
555 | else | 577 | |
556 | prog->redirections[i].fd = 0; | 578 | if (prog->redirections[i].fd == -1) { |
557 | } | 579 | if (*src == '>') |
558 | 580 | prog->redirections[i].fd = 1; | |
559 | if (*src++ == '>') { | 581 | else |
560 | if (*src == '>') | 582 | prog->redirections[i].fd = 0; |
561 | prog->redirections[i].type = REDIRECT_APPEND, src++; | 583 | } |
562 | else | 584 | |
563 | prog->redirections[i].type = REDIRECT_OVERWRITE; | 585 | if (*src++ == '>') { |
564 | } else { | 586 | if (*src == '>') |
565 | prog->redirections[i].type = REDIRECT_INPUT; | 587 | prog->redirections[i].type = |
566 | } | 588 | REDIRECT_APPEND, src++; |
567 | 589 | else | |
568 | /* This isn't POSIX sh compliant. Oh well. */ | 590 | prog->redirections[i].type = REDIRECT_OVERWRITE; |
569 | chptr = src; | 591 | } else { |
570 | while (isspace(*chptr)) chptr++; | 592 | prog->redirections[i].type = REDIRECT_INPUT; |
571 | 593 | } | |
572 | if (!*chptr) { | 594 | |
573 | fprintf(stderr, "file name expected after %c\n", *src); | 595 | /* This isn't POSIX sh compliant. Oh well. */ |
574 | freeJob(job); | 596 | chptr = src; |
575 | return 1; | 597 | while (isspace(*chptr)) |
576 | } | 598 | chptr++; |
577 | 599 | ||
578 | prog->redirections[i].filename = buf; | 600 | if (!*chptr) { |
579 | while (*chptr && !isspace(*chptr)) | 601 | fprintf(stderr, "file name expected after %c\n", *src); |
580 | *buf++ = *chptr++; | 602 | freeJob(job); |
581 | 603 | return 1; | |
582 | src = chptr - 1; /* we src++ later */ | 604 | } |
583 | prog->argv[argc] = ++buf; | 605 | |
584 | break; | 606 | prog->redirections[i].filename = buf; |
585 | 607 | while (*chptr && !isspace(*chptr)) | |
586 | case '|': /* pipe */ | 608 | *buf++ = *chptr++; |
587 | /* finish this command */ | 609 | |
588 | if (*prog->argv[argc]) argc++; | 610 | src = chptr - 1; /* we src++ later */ |
589 | if (!argc) { | 611 | prog->argv[argc] = ++buf; |
590 | fprintf(stderr, "empty command in pipe\n"); | 612 | break; |
591 | freeJob(job); | 613 | |
592 | return 1; | 614 | case '|': /* pipe */ |
593 | } | 615 | /* finish this command */ |
594 | prog->argv[argc] = NULL; | 616 | if (*prog->argv[argc]) |
595 | 617 | argc++; | |
596 | /* and start the next */ | 618 | if (!argc) { |
597 | job->numProgs++; | 619 | fprintf(stderr, "empty command in pipe\n"); |
598 | job->progs = realloc(job->progs, | 620 | freeJob(job); |
599 | sizeof(*job->progs) * job->numProgs); | 621 | return 1; |
600 | prog = job->progs + (job->numProgs - 1); | 622 | } |
601 | prog->numRedirections = 0; | 623 | prog->argv[argc] = NULL; |
602 | prog->redirections = NULL; | 624 | |
603 | prog->freeGlob = 0; | 625 | /* and start the next */ |
604 | argc = 0; | 626 | job->numProgs++; |
605 | 627 | job->progs = realloc(job->progs, | |
606 | argvAlloced = 5; | 628 | sizeof(*job->progs) * job->numProgs); |
607 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | 629 | prog = job->progs + (job->numProgs - 1); |
608 | prog->argv[0] = ++buf; | 630 | prog->numRedirections = 0; |
609 | 631 | prog->redirections = NULL; | |
610 | src++; | 632 | prog->freeGlob = 0; |
611 | while (*src && isspace(*src)) src++; | 633 | argc = 0; |
612 | 634 | ||
613 | if (!*src) { | 635 | argvAlloced = 5; |
614 | fprintf(stderr, "empty command in pipe\n"); | 636 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); |
615 | return 1; | 637 | prog->argv[0] = ++buf; |
616 | } | 638 | |
617 | src--; /* we'll ++ it at the end of the loop */ | 639 | src++; |
618 | 640 | while (*src && isspace(*src)) | |
619 | break; | 641 | src++; |
620 | 642 | ||
621 | case '&': /* background */ | 643 | if (!*src) { |
622 | *isBg = 1; | 644 | fprintf(stderr, "empty command in pipe\n"); |
623 | case ';': /* multiple commands */ | 645 | return 1; |
624 | done = 1; | 646 | } |
625 | returnCommand = *commandPtr + (src - *commandPtr) + 1; | 647 | src--; /* we'll ++ it at the end of the loop */ |
626 | break; | 648 | |
627 | 649 | break; | |
628 | case '\\': | 650 | |
629 | src++; | 651 | case '&': /* background */ |
630 | if (!*src) { | 652 | *isBg = 1; |
631 | freeJob(job); | 653 | case ';': /* multiple commands */ |
632 | fprintf(stderr, "character expected after \\\n"); | 654 | done = 1; |
633 | return 1; | 655 | returnCommand = *commandPtr + (src - *commandPtr) + 1; |
634 | } | 656 | break; |
635 | if (*src == '*' || *src == '[' || *src == ']' || *src == '?') | 657 | |
636 | *buf++ = '\\'; | 658 | case '\\': |
637 | /* fallthrough */ | 659 | src++; |
638 | default: | 660 | if (!*src) { |
639 | *buf++ = *src; | 661 | freeJob(job); |
640 | } | 662 | fprintf(stderr, "character expected after \\\n"); |
641 | 663 | return 1; | |
642 | src++; | 664 | } |
665 | if (*src == '*' || *src == '[' || *src == ']' | ||
666 | || *src == '?') *buf++ = '\\'; | ||
667 | /* fallthrough */ | ||
668 | default: | ||
669 | *buf++ = *src; | ||
670 | } | ||
671 | |||
672 | src++; | ||
643 | } | 673 | } |
644 | 674 | ||
645 | if (*prog->argv[argc]) { | 675 | if (*prog->argv[argc]) { |
646 | argc++; | 676 | argc++; |
647 | globLastArgument(prog, &argc, &argvAlloced); | 677 | globLastArgument(prog, &argc, &argvAlloced); |
648 | } | 678 | } |
649 | if (!argc) { | 679 | if (!argc) { |
650 | freeJob(job); | 680 | freeJob(job); |
651 | return 0; | 681 | return 0; |
652 | } | 682 | } |
653 | prog->argv[argc] = NULL; | 683 | prog->argv[argc] = NULL; |
654 | 684 | ||
655 | if (!returnCommand) { | 685 | if (!returnCommand) { |
656 | job->text = malloc(strlen(*commandPtr) + 1); | 686 | job->text = malloc(strlen(*commandPtr) + 1); |
657 | strcpy(job->text, *commandPtr); | 687 | strcpy(job->text, *commandPtr); |
658 | } else { | 688 | } else { |
659 | /* This leaves any trailing spaces, which is a bit sloppy */ | 689 | /* This leaves any trailing spaces, which is a bit sloppy */ |
660 | 690 | ||
661 | count = returnCommand - *commandPtr; | 691 | count = returnCommand - *commandPtr; |
662 | job->text = malloc(count + 1); | 692 | job->text = malloc(count + 1); |
663 | strncpy(job->text, *commandPtr, count); | 693 | strncpy(job->text, *commandPtr, count); |
664 | job->text[count] = '\0'; | 694 | job->text[count] = '\0'; |
665 | } | 695 | } |
666 | 696 | ||
667 | *commandPtr = returnCommand; | 697 | *commandPtr = returnCommand; |
@@ -669,63 +699,64 @@ static int parseCommand(char ** commandPtr, struct job * job, int * isBg) | |||
669 | return 0; | 699 | return 0; |
670 | } | 700 | } |
671 | 701 | ||
672 | static int runCommand(struct job newJob, struct jobSet * jobList, | 702 | static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) |
673 | int inBg) | ||
674 | { | 703 | { |
675 | struct job * job; | 704 | struct job *job; |
676 | int i; | 705 | int i; |
677 | int nextin, nextout; | 706 | int nextin, nextout; |
678 | int pipefds[2]; /* pipefd[0] is for reading */ | 707 | int pipefds[2]; /* pipefd[0] is for reading */ |
679 | struct builtInCommand *x; | 708 | struct builtInCommand *x; |
680 | 709 | ||
681 | /* handle built-ins here -- we don't fork() so we can't background | 710 | /* handle built-ins here -- we don't fork() so we can't background |
682 | these very easily */ | 711 | these very easily */ |
683 | for( x=bltins ; x->cmd ; x++) { | 712 | for (x = bltins; x->cmd; x++) { |
684 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { | 713 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { |
685 | return(x->function(&newJob, jobList)); | 714 | return (x->function(&newJob, jobList)); |
686 | } | 715 | } |
687 | } | 716 | } |
688 | 717 | ||
689 | nextin = 0, nextout = 1; | 718 | nextin = 0, nextout = 1; |
690 | for (i = 0; i < newJob.numProgs; i++) { | 719 | for (i = 0; i < newJob.numProgs; i++) { |
691 | if ((i + 1) < newJob.numProgs) { | 720 | if ((i + 1) < newJob.numProgs) { |
692 | pipe(pipefds); | 721 | pipe(pipefds); |
693 | nextout = pipefds[1]; | 722 | nextout = pipefds[1]; |
694 | } else { | 723 | } else { |
695 | nextout = 1; | 724 | nextout = 1; |
696 | } | 725 | } |
697 | 726 | ||
698 | if (!(newJob.progs[i].pid = fork())) { | 727 | if (!(newJob.progs[i].pid = fork())) { |
699 | signal(SIGTTOU, SIG_DFL); | 728 | signal(SIGTTOU, SIG_DFL); |
700 | 729 | ||
701 | if (nextin != 0) { | 730 | if (nextin != 0) { |
702 | dup2(nextin, 0); | 731 | dup2(nextin, 0); |
703 | close(nextin); | 732 | close(nextin); |
704 | } | 733 | } |
705 | 734 | ||
706 | if (nextout != 1) { | 735 | if (nextout != 1) { |
707 | dup2(nextout, 1); | 736 | dup2(nextout, 1); |
708 | close(nextout); | 737 | close(nextout); |
709 | } | 738 | } |
710 | 739 | ||
711 | /* explicit redirections override pipes */ | 740 | /* explicit redirections override pipes */ |
712 | setupRedirections(newJob.progs + i); | 741 | setupRedirections(newJob.progs + i); |
713 | 742 | ||
714 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); | 743 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); |
715 | fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0], | 744 | fatalError("sh: %s: %s\n", newJob.progs[i].argv[0], |
716 | strerror(errno)); | 745 | strerror(errno)); |
717 | } | 746 | } |
718 | 747 | ||
719 | /* put our child in the process group whose leader is the | 748 | /* put our child in the process group whose leader is the |
720 | first process in this pipe */ | 749 | first process in this pipe */ |
721 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); | 750 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); |
722 | 751 | ||
723 | if (nextin != 0) close(nextin); | 752 | if (nextin != 0) |
724 | if (nextout != 1) close(nextout); | 753 | close(nextin); |
725 | 754 | if (nextout != 1) | |
726 | /* If there isn't another process, nextin is garbage | 755 | close(nextout); |
727 | but it doesn't matter */ | 756 | |
728 | nextin = pipefds[0]; | 757 | /* If there isn't another process, nextin is garbage |
758 | but it doesn't matter */ | ||
759 | nextin = pipefds[0]; | ||
729 | } | 760 | } |
730 | 761 | ||
731 | newJob.pgrp = newJob.progs[0].pid; | 762 | newJob.pgrp = newJob.progs[0].pid; |
@@ -733,16 +764,16 @@ static int runCommand(struct job newJob, struct jobSet * jobList, | |||
733 | /* find the ID for the job to use */ | 764 | /* find the ID for the job to use */ |
734 | newJob.jobId = 1; | 765 | newJob.jobId = 1; |
735 | for (job = jobList->head; job; job = job->next) | 766 | for (job = jobList->head; job; job = job->next) |
736 | if (job->jobId >= newJob.jobId) | 767 | if (job->jobId >= newJob.jobId) |
737 | newJob.jobId = job->jobId + 1; | 768 | newJob.jobId = job->jobId + 1; |
738 | 769 | ||
739 | /* add the job to the list of running jobs */ | 770 | /* add the job to the list of running jobs */ |
740 | if (!jobList->head) { | 771 | if (!jobList->head) { |
741 | job = jobList->head = malloc(sizeof(*job)); | 772 | job = jobList->head = malloc(sizeof(*job)); |
742 | } else { | 773 | } else { |
743 | for (job = jobList->head; job->next; job = job->next); | 774 | for (job = jobList->head; job->next; job = job->next); |
744 | job->next = malloc(sizeof(*job)); | 775 | job->next = malloc(sizeof(*job)); |
745 | job = job->next; | 776 | job = job->next; |
746 | } | 777 | } |
747 | 778 | ||
748 | *job = newJob; | 779 | *job = newJob; |
@@ -751,165 +782,177 @@ static int runCommand(struct job newJob, struct jobSet * jobList, | |||
751 | job->stoppedProgs = 0; | 782 | job->stoppedProgs = 0; |
752 | 783 | ||
753 | if (inBg) { | 784 | if (inBg) { |
754 | /* we don't wait for background jobs to return -- append it | 785 | /* we don't wait for background jobs to return -- append it |
755 | to the list of backgrounded jobs and leave it alone */ | 786 | to the list of backgrounded jobs and leave it alone */ |
756 | 787 | ||
757 | printf("[%d] %d\n", job->jobId, | 788 | printf("[%d] %d\n", job->jobId, |
758 | newJob.progs[newJob.numProgs - 1].pid); | 789 | newJob.progs[newJob.numProgs - 1].pid); |
759 | } else { | 790 | } else { |
760 | jobList->fg = job; | 791 | jobList->fg = job; |
792 | |||
793 | /* move the new process group into the foreground */ | ||
761 | 794 | ||
762 | /* move the new process group into the foreground */ | 795 | if (tcsetpgrp(0, newJob.pgrp)) |
763 | 796 | perror("tcsetpgrp"); | |
764 | if (tcsetpgrp(0, newJob.pgrp)) | ||
765 | perror("tcsetpgrp"); | ||
766 | } | 797 | } |
767 | 798 | ||
768 | return 0; | 799 | return 0; |
769 | } | 800 | } |
770 | 801 | ||
771 | static int setupRedirections(struct childProgram * prog) | 802 | static int setupRedirections(struct childProgram *prog) |
772 | { | 803 | { |
773 | int i; | 804 | int i; |
774 | int openfd; | 805 | int openfd; |
775 | int mode=O_RDONLY; | 806 | int mode = O_RDONLY; |
776 | struct redirectionSpecifier * redir = prog->redirections; | 807 | struct redirectionSpecifier *redir = prog->redirections; |
777 | 808 | ||
778 | for (i = 0; i < prog->numRedirections; i++, redir++) { | 809 | for (i = 0; i < prog->numRedirections; i++, redir++) { |
779 | switch (redir->type) { | 810 | switch (redir->type) { |
780 | case REDIRECT_INPUT: | 811 | case REDIRECT_INPUT: |
781 | mode = O_RDONLY; | 812 | mode = O_RDONLY; |
782 | break; | 813 | break; |
783 | case REDIRECT_OVERWRITE: | 814 | case REDIRECT_OVERWRITE: |
784 | mode = O_RDWR | O_CREAT | O_TRUNC; | 815 | mode = O_RDWR | O_CREAT | O_TRUNC; |
785 | break; | 816 | break; |
786 | case REDIRECT_APPEND: | 817 | case REDIRECT_APPEND: |
787 | mode = O_RDWR | O_CREAT | O_APPEND; | 818 | mode = O_RDWR | O_CREAT | O_APPEND; |
788 | break; | 819 | break; |
789 | } | 820 | } |
790 | 821 | ||
791 | openfd = open(redir->filename, mode, 0666); | 822 | openfd = open(redir->filename, mode, 0666); |
792 | if (openfd < 0) { | 823 | if (openfd < 0) { |
793 | /* this could get lost if stderr has been redirected, but | 824 | /* this could get lost if stderr has been redirected, but |
794 | bash and ash both lose it as well (though zsh doesn't!) */ | 825 | bash and ash both lose it as well (though zsh doesn't!) */ |
795 | fprintf(stderr, "error opening %s: %s\n", redir->filename, | 826 | fprintf(stderr, "error opening %s: %s\n", redir->filename, |
796 | strerror(errno)); | 827 | strerror(errno)); |
797 | return 1; | 828 | return 1; |
798 | } | 829 | } |
799 | 830 | ||
800 | if (openfd != redir->fd) { | 831 | if (openfd != redir->fd) { |
801 | dup2(openfd, redir->fd); | 832 | dup2(openfd, redir->fd); |
802 | close(openfd); | 833 | close(openfd); |
803 | } | 834 | } |
804 | } | 835 | } |
805 | 836 | ||
806 | return 0; | 837 | return 0; |
807 | } | 838 | } |
808 | 839 | ||
809 | 840 | ||
810 | static int busy_loop(FILE * input) | 841 | static int busy_loop(FILE * input) |
811 | { | 842 | { |
812 | char command[MAX_COMMAND_LEN + 1]; | 843 | char *command; |
813 | char * nextCommand = NULL; | 844 | char *nextCommand = NULL; |
814 | struct jobSet jobList = { NULL, NULL }; | 845 | struct jobSet jobList = { NULL, NULL }; |
815 | struct job newJob; | 846 | struct job newJob; |
816 | int i; | 847 | int i; |
817 | int status; | 848 | int status; |
818 | int inBg; | 849 | int inBg; |
819 | 850 | ||
851 | command = (char*) calloc(BUFSIZ, sizeof(char)); | ||
852 | |||
820 | /* don't pay any attention to this signal; it just confuses | 853 | /* don't pay any attention to this signal; it just confuses |
821 | things and isn't really meant for shells anyway */ | 854 | things and isn't really meant for shells anyway */ |
822 | signal(SIGTTOU, SIG_IGN); | 855 | signal(SIGTTOU, SIG_IGN); |
823 | 856 | ||
824 | while (1) { | 857 | while (1) { |
825 | if (!jobList.fg) { | 858 | if (!jobList.fg) { |
826 | /* no job is in the foreground */ | 859 | /* no job is in the foreground */ |
827 | 860 | ||
828 | /* see if any background processes have exited */ | 861 | /* see if any background processes have exited */ |
829 | checkJobs(&jobList); | 862 | checkJobs(&jobList); |
830 | 863 | ||
831 | if (!nextCommand) { | 864 | if (!nextCommand) { |
832 | if (getCommand(input, command)) break; | 865 | if (getCommand(input, command)) |
833 | nextCommand = command; | 866 | break; |
834 | } | 867 | nextCommand = command; |
835 | 868 | } | |
836 | if (!parseCommand(&nextCommand, &newJob, &inBg) && | 869 | |
837 | newJob.numProgs) { | 870 | if (!parseCommand(&nextCommand, &newJob, &inBg) && |
838 | runCommand(newJob, &jobList, inBg); | 871 | newJob.numProgs) { |
839 | } | 872 | runCommand(newJob, &jobList, inBg); |
840 | } else { | 873 | } |
841 | /* a job is running in the foreground; wait for it */ | 874 | } else { |
842 | i = 0; | 875 | /* a job is running in the foreground; wait for it */ |
843 | while (!jobList.fg->progs[i].pid || | 876 | i = 0; |
844 | jobList.fg->progs[i].isStopped) i++; | 877 | while (!jobList.fg->progs[i].pid || |
845 | 878 | jobList.fg->progs[i].isStopped) i++; | |
846 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); | 879 | |
847 | 880 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); | |
848 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | 881 | |
849 | /* the child exited */ | 882 | if (WIFEXITED(status) || WIFSIGNALED(status)) { |
850 | jobList.fg->runningProgs--; | 883 | /* the child exited */ |
851 | jobList.fg->progs[i].pid = 0; | 884 | jobList.fg->runningProgs--; |
852 | 885 | jobList.fg->progs[i].pid = 0; | |
853 | if (!jobList.fg->runningProgs) { | 886 | |
854 | /* child exited */ | 887 | if (!jobList.fg->runningProgs) { |
855 | 888 | /* child exited */ | |
856 | removeJob(&jobList, jobList.fg); | 889 | |
857 | jobList.fg = NULL; | 890 | removeJob(&jobList, jobList.fg); |
858 | 891 | jobList.fg = NULL; | |
859 | /* move the shell to the foreground */ | 892 | |
860 | if (tcsetpgrp(0, getpid())) | 893 | /* move the shell to the foreground */ |
861 | perror("tcsetpgrp"); | 894 | if (tcsetpgrp(0, getpid())) |
862 | } | 895 | perror("tcsetpgrp"); |
863 | } else { | 896 | } |
864 | /* the child was stopped */ | 897 | } else { |
865 | jobList.fg->stoppedProgs++; | 898 | /* the child was stopped */ |
866 | jobList.fg->progs[i].isStopped = 1; | 899 | jobList.fg->stoppedProgs++; |
867 | 900 | jobList.fg->progs[i].isStopped = 1; | |
868 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { | 901 | |
869 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, | 902 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { |
870 | "Stopped", jobList.fg->text); | 903 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, |
871 | jobList.fg = NULL; | 904 | "Stopped", jobList.fg->text); |
872 | } | 905 | jobList.fg = NULL; |
873 | } | 906 | } |
874 | 907 | } | |
875 | if (!jobList.fg) { | 908 | |
876 | /* move the shell to the foreground */ | 909 | if (!jobList.fg) { |
877 | if (tcsetpgrp(0, getpid())) | 910 | /* move the shell to the foreground */ |
878 | perror("tcsetpgrp"); | 911 | if (tcsetpgrp(0, getpid())) |
879 | } | 912 | perror("tcsetpgrp"); |
880 | } | 913 | } |
881 | } | 914 | } |
915 | } | ||
916 | free( command); | ||
882 | 917 | ||
883 | return 0; | 918 | return 0; |
884 | } | 919 | } |
885 | 920 | ||
886 | 921 | ||
887 | int shell_main(int argc, char ** argv) | 922 | int shell_main(int argc, char **argv) |
888 | { | 923 | { |
889 | FILE * input = stdin; | 924 | FILE *input = stdin; |
890 | 925 | ||
891 | if (argc > 2) { | 926 | if (argc > 2) { |
892 | usage( shell_usage); | 927 | usage(shell_usage); |
893 | } | 928 | } |
894 | /* initialize the cwd */ | 929 | /* initialize the cwd */ |
895 | getcwd(cwd, sizeof(cwd)); | 930 | getcwd(cwd, sizeof(cwd)); |
896 | 931 | ||
897 | 932 | ||
898 | //if (argv[0] && argv[0][0] == '-') { | 933 | //if (argv[0] && argv[0][0] == '-') { |
899 | // shell_source("/etc/profile"); | 934 | // shell_source("/etc/profile"); |
900 | //} | 935 | //} |
901 | 936 | ||
902 | if (argc < 2) { | 937 | if (argc < 2) { |
903 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); | 938 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, |
904 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); | 939 | BB_BT); |
905 | } else { | 940 | fprintf(stdout, |
906 | input = fopen(argv[1], "r"); | 941 | "Enter 'help' for a list of built-in commands.\n\n"); |
907 | if (!input) | 942 | } else { |
908 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno)); | 943 | input = fopen(argv[1], "r"); |
909 | // else | 944 | if (!input) |
910 | // fatalError("Got it.\n"); | 945 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], |
911 | //exit(shell_source(argv[1])); | 946 | strerror(errno)); |
912 | } | 947 | // else |
913 | 948 | // fatalError("Got it.\n"); | |
914 | return (busy_loop( input)); | 949 | //exit(shell_source(argv[1])); |
950 | |||
951 | /* Set terminal IO to canonical mode, and save old term settings. */ | ||
952 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | ||
953 | cmdedit_init(); | ||
954 | #endif | ||
955 | } | ||
956 | |||
957 | return (busy_loop(input)); | ||
915 | } | 958 | } |