/* minish2.c - example of minimalistic shell * * script filename may be given as argument, else reads stdin * understands simple command language: * (1) leading spaces are ignored * (2) leading '#' (also after spaces) means a comment line * (3) trailing '&' means background execution * (4) one internal command, 'exit' with optional argument for status code * (5) external commands, searches PATH (but doesn't allow changing it) */ #include #include #include #include #include #include #define MAXLINE 300 int main(int argc, char **argv) { char line[MAXLINE+1]; /* command line being executed */ char *args[MAXLINE]; /* pointers to the beginning of arguments */ char *p, **arg; /* pointers for above arrays */ int bg; /* indicates command is being backgrounded */ int pid; /* PID of child process */ /* open file argument in place of stdin */ if (argc>=2 && !freopen(argv[1], "r", stdin)) { perror(argv[1]); exit(errno); } /* read lines until EOF */ while (fgets(line, MAXLINE, stdin)) { /* cleanup after possible terminated background processes */ waitpid(-1, NULL, WNOHANG); /* verify we got \n */ if ((p = strchr(line, '\n'))) { *p=0; /* discard it */ } else { /* nope, line must've been too long */ fprintf(stderr, "line too long\n"); exit(1); } /* remove possible trailing & and leave bg marking it was there */ /* note: this does not allow spaces after the & */ if ((bg = (*--p == '&'))) *p=0; /* skip leading spaces */ p = line; while (*p == ' ') ++p; /* if next char is # the line is comment, skip it */ if (*p == '#') continue; /* p points now at first word in line, save it in args[0] */ *(arg=args) = p; /* search spaces */ while ((p = strchr(p, ' '))) { /* clear all adjacent spaces */ while (*p == ' ') *p++ = 0; /* if a non-space follows it's an argument, save */ /* pointer to it to next element of args */ if (*p) *++arg=p; } /* need NULL at end of args for execv */ *++arg=NULL; /* if command is "exit", exit! */ if (!strcmp(line,"exit")) { exit(args[1] ? atoi(args[1]) : 0); } /* start new process */ switch ((pid=fork())) { case 0: /* we're in child process now */ /* try to execute the command */ execvp(args[0], args); /* won't return if succesful */ perror(args[0]); exit(errno); case -1: /* out of memory or something */ perror("fork() failed"); break; default: /* wait for the child process to finish, unless */ /* there was & meaning it should run in the background */ if (!bg) waitpid(pid, NULL, 0); } } exit(0); } /******************************The End******************************/