#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define DEBUG 1
#define MAXLINELEN 4096
#define MAXARGS 128
#define END_OF_LINE 0
#define SEQ_OP ';'
#define SEQUENCE 1
#define BG_OP '&'
#define BACKGROUND 2
struct cmd {
struct cmd *next;
int terminator;
char *exe_path;
int nargs;
char *arg[MAXARGS];
};
void *ck_malloc(size_t size)
{
void *ret = malloc(size);
if (!ret) {
perror("dumbshell:ck_malloc");
exit(1);
}
return ret;
}
char *skip_to_non_ws(char *p)
{
int ch;
while (ch = *p) {
if (ch != ' ' && ch != '\t' && ch != '\n') return p;
++p;
}
return 0;
}
char *skip_to_ws_or_sep(char *p)
{
int ch;
while (ch = *p) {
if (ch == ' ' || ch == '\t' || ch == '\n') return p;
if (ch == SEQ_OP || ch == BG_OP) return p;
++p;
}
return 0;
}
struct cmd *parse_commands(char *line)
{
char *ptr;
int ix;
struct cmd *cur;
ptr = skip_to_non_ws(line);
if (!ptr) return 0;
cur = ck_malloc(sizeof *cur);
cur->next = 0;
cur->exe_path = ptr;
cur->arg[0] = ptr;
cur->terminator = END_OF_LINE;
ix = 1;
for (;;) {
ptr = skip_to_ws_or_sep(ptr);
if (!ptr) {
break;
}
if (*ptr == SEQ_OP) {
*ptr = 0;
cur->next = parse_commands(ptr+1);
if (cur->next) {
cur->terminator = SEQUENCE;
}
break;
} else if (*ptr == BG_OP) {
*ptr = 0;
cur->next = parse_commands(ptr+1);
cur->terminator = BACKGROUND;
break;
}
*ptr = 0;
ptr = skip_to_non_ws(ptr+1);
if (!ptr) {
break;
}
if (*ptr == SEQ_OP) {
/* found a sequence operator */
cur->next = parse_commands(ptr+1);
if (cur->next) {
cur->terminator = SEQUENCE;
}
break;
} else if (*ptr == BG_OP) {
cur->next = parse_commands(ptr+1);
cur->terminator = BACKGROUND;
break;
}
cur->arg[ix] = ptr;
++ix;
}
cur->arg[ix] = 0;
cur->nargs = ix;
return cur;
}
void execute(struct cmd *clist)
{
int pid, npid, stat;
pid = fork();
if (pid == -1) {
perror("dumbshell:fork");
exit(1);
}
if (!pid) {
/* child */
execvp(clist->exe_path,clist->arg);
fprintf(stderr,"No such command: %s\n",clist->exe_path);
exit(1);
}
if (clist->terminator != BACKGROUND) do {
npid = wait(&stat);
printf("Process %d exited with status %d\n",npid,stat);
} while (npid != pid);
switch (clist->terminator) {
case SEQUENCE:
execute(clist->next);
break;
case BACKGROUND:
if (clist->next) execute(clist->next);
}
}
void free_commands(struct cmd *clist)
{
struct cmd *nxt;
do {
nxt = clist->next;
free(clist);
clist = nxt;
} while (clist);
}
char *get_command(char *buf,
int size,
FILE *in)
{
if (in == stdin) {
fputs("@ ",stdout); /* prompt */
}
return fgets(buf,size,in);
}
void main(void)
{
char linebuf[MAXLINELEN];
struct cmd *commands;
while (get_command(linebuf,MAXLINELEN,stdin) != NULL) {
commands = parse_commands(linebuf);
if (commands) {
execute(commands);
free_commands(commands);
}
}
}

bsy@cse.ucsd.edu, last updated