#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.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);
		if (WIFEXITED(stat))
			printf("Process %d exited with status %d\n",npid,WEXITSTATUS(stat));
		else if (WIFSIGNALED(stat))
			printf("Process %d kill with signal %d\n",npid,WTERMSIG(stat));
		if (WCOREDUMP(stat))
			printf("Core dumped.\n");
	} 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);
		}
	}
}

[ CSE 80 | ACS home | CSE home | CSE calendar | bsy's home page ]
picture of bsy

bsy@cse.ucsd.edu, last updated Thu Feb 6 11:16:57 PST 1997.

email bsy