aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README22
-rw-r--r--myint.c36
-rw-r--r--myspin.c24
-rw-r--r--mysplit.c35
-rw-r--r--mystop.c36
-rwxr-xr-xsdriver.pl210
-rw-r--r--trace01.txt5
-rw-r--r--trace02.txt5
-rw-r--r--trace03.txt5
-rw-r--r--trace04.txt5
-rw-r--r--trace05.txt11
-rw-r--r--trace06.txt8
-rw-r--r--trace07.txt14
-rw-r--r--trace08.txt14
-rw-r--r--trace09.txt20
-rw-r--r--trace10.txt22
-rw-r--r--trace11.txt12
-rw-r--r--trace12.txt17
-rw-r--r--trace13.txt23
-rw-r--r--trace14.txt47
-rw-r--r--trace15.txt46
-rw-r--r--trace16.txt16
-rw-r--r--tsh.c509
-rwxr-xr-xtshrefbin0 -> 19278 bytes
24 files changed, 1142 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..a5add38
--- /dev/null
+++ b/README
@@ -0,0 +1,22 @@
+################
+CS:APP Shell Lab
+################
+
+Files:
+
+Makefile # Compiles your shell program and runs the tests
+README # This file
+tsh.c # The shell program that you will write and hand in
+tshref # The reference shell binary.
+
+# The remaining files are used to test your shell
+sdriver.pl # The trace-driven shell driver
+trace*.txt # The 15 trace files that control the shell driver
+tshref.out # Example output of the reference shell on all 15 traces
+
+# Little C programs that are called by the trace files
+myspin.c # Takes argument <n> and spins for <n> seconds
+mysplit.c # Forks a child that spins for <n> seconds
+mystop.c # Spins for <n> seconds and sends SIGTSTP to itself
+myint.c # Spins for <n> seconds and sends SIGINT to itself
+
diff --git a/myint.c b/myint.c
new file mode 100644
index 0000000..ea5bee1
--- /dev/null
+++ b/myint.c
@@ -0,0 +1,36 @@
+/*
+ * myint.c - Another handy routine for testing your tiny shell
+ *
+ * usage: myint <n>
+ * Sleeps for <n> seconds and sends SIGINT to itself.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+int main(int argc, char **argv)
+{
+ int i, secs;
+ pid_t pid;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <n>\n", argv[0]);
+ exit(0);
+ }
+ secs = atoi(argv[1]);
+
+ for (i=0; i < secs; i++)
+ sleep(1);
+
+ pid = getpid();
+
+ if (kill(pid, SIGINT) < 0)
+ fprintf(stderr, "kill (int) error");
+
+ exit(0);
+
+}
diff --git a/myspin.c b/myspin.c
new file mode 100644
index 0000000..08c3e0c
--- /dev/null
+++ b/myspin.c
@@ -0,0 +1,24 @@
+/*
+ * myspin.c - A handy program for testing your tiny shell
+ *
+ * usage: myspin <n>
+ * Sleeps for <n> seconds in 1-second chunks.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ int i, secs;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <n>\n", argv[0]);
+ exit(0);
+ }
+ secs = atoi(argv[1]);
+ for (i=0; i < secs; i++)
+ sleep(1);
+ exit(0);
+}
diff --git a/mysplit.c b/mysplit.c
new file mode 100644
index 0000000..9ac016e
--- /dev/null
+++ b/mysplit.c
@@ -0,0 +1,35 @@
+/*
+ * mysplit.c - Another handy routine for testing your tiny shell
+ *
+ * usage: mysplit <n>
+ * Fork a child that spins for <n> seconds in 1-second chunks.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+int main(int argc, char **argv)
+{
+ int i, secs;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <n>\n", argv[0]);
+ exit(0);
+ }
+ secs = atoi(argv[1]);
+
+
+ if (fork() == 0) { /* child */
+ for (i=0; i < secs; i++)
+ sleep(1);
+ exit(0);
+ }
+
+ /* parent waits for child to terminate */
+ wait(NULL);
+
+ exit(0);
+}
diff --git a/mystop.c b/mystop.c
new file mode 100644
index 0000000..12eee5f
--- /dev/null
+++ b/mystop.c
@@ -0,0 +1,36 @@
+/*
+ * mystop.c - Another handy routine for testing your tiny shell
+ *
+ * usage: mystop <n>
+ * Sleeps for <n> seconds and sends SIGTSTP to itself.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+int main(int argc, char **argv)
+{
+ int i, secs;
+ pid_t pid;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <n>\n", argv[0]);
+ exit(0);
+ }
+ secs = atoi(argv[1]);
+
+ for (i=0; i < secs; i++)
+ sleep(1);
+
+ pid = getpid();
+
+ if (kill(-pid, SIGTSTP) < 0)
+ fprintf(stderr, "kill (tstp) error");
+
+ exit(0);
+
+}
diff --git a/sdriver.pl b/sdriver.pl
new file mode 100755
index 0000000..852bf3d
--- /dev/null
+++ b/sdriver.pl
@@ -0,0 +1,210 @@
+#!/usr/bin/perl
+#!/usr/local/bin/perl
+use Getopt::Std;
+use FileHandle;
+use IPC::Open2;
+
+#######################################################################
+# sdriver.pl - Shell driver
+#
+# Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
+# May not be used, modified, or copied without permission.
+#
+# The driver runs a student's shell program as a child, sends
+# commands and signals to the child as directed by a trace file,
+# and captures and displays the output produced by the child.
+#
+# Tracefile format:
+#
+# The tracefile consists of text lines that are either blank lines,
+# comment lines, driver commands, or shell commands. Blank lines are
+# ignored. Comment lines begin with "#" and are echo'd without change
+# to stdout. Driver commands are intepreted by the driver and are not
+# passed to the child shell. All other lines are shell commands and
+# are passed without modification to the shell, which reads them on
+# stdin. Output produced by the child on stdout/stderr is read by
+# the parent and printed on its stdout.
+#
+# Driver commands:
+# TSTP Send a SIGTSTP signal to the child
+# INT Send a SIGINT signal to the child
+# QUIT Send a SIGQUIT signal to the child
+# KILL Send a SIGKILL signal to the child
+# CLOSE Close Writer (sends EOF signal to child)
+# WAIT Wait() for child to terminate
+# SLEEP <n> Sleep for <n> seconds
+#
+######################################################################
+
+#
+# usage - print help message and terminate
+#
+sub usage
+{
+ printf STDERR "$_[0]\n";
+ printf STDERR "Usage: $0 [-hv] -t <trace> -s <shellprog> -a <args>\n";
+ printf STDERR "Options:\n";
+ printf STDERR " -h Print this message\n";
+ printf STDERR " -v Be more verbose\n";
+ printf STDERR " -t <trace> Trace file\n";
+ printf STDERR " -s <shell> Shell program to test\n";
+ printf STDERR " -a <args> Shell arguments\n";
+ printf STDERR " -g Generate output for autograder\n";
+ die "\n" ;
+}
+
+# Parse the command line arguments
+getopts('hgvt:s:a:');
+if ($opt_h) {
+ usage();
+}
+if (!$opt_t) {
+ usage("Missing required -t argument");
+}
+if (!$opt_s) {
+ usage("Missing required -s argument");
+}
+$verbose = $opt_v;
+$infile = $opt_t;
+$shellprog = $opt_s;
+$shellargs = $opt_a;
+$grade = $opt_g;
+
+# Make sure the input script exists and is readable
+-e $infile
+ or die "$0: ERROR: $infile not found\n";
+-r $infile
+ or die "$0: ERROR: $infile is not readable\n";
+
+# Make sure the shell program exists and is executable
+-e $shellprog
+ or die "$0: ERROR: $shellprog not found\n";
+-x $shellprog
+ or die "$0: ERROR: $shellprog is not executable\n";
+
+
+# Open the input script
+open INFILE, $infile
+ or die "$0: ERROR: Couldn't open input file $infile: $!\n";
+
+#
+# Fork a child, run the shell in it, and connect the parent
+# and child with a pair of unidirectional pipes:
+# parent:Writer -> child:stdin
+# child:stdout -> parent:Reader
+#
+$pid = open2(\*Reader, \*Writer, "$shellprog $shellargs");
+Writer->autoflush();
+
+# The autograder will want to know the child shell's pid
+if ($grade) {
+ print ("pid=$pid\n");
+}
+
+#
+# Parent reads a trace file, sends commands to the child shell.
+#
+while (<INFILE>) {
+ $line = $_;
+ chomp($line);
+
+ # Comment line
+ if ($line =~ /^#/) {
+ print "$line\n";
+ }
+
+ # Blank line
+ elsif ($line =~ /^\s*$/) {
+ if ($verbose) {
+ print "$0: Ignoring blank line\n";
+ }
+ }
+
+ # Send SIGTSTP (ctrl-z)
+ elsif ($line =~ /TSTP/) {
+ if ($verbose) {
+ print "$0: Sending SIGTSTP signal to process $pid\n";
+ }
+ kill 'TSTP', $pid;
+ }
+
+ # Send SIGINT (ctrl-c)
+ elsif ($line =~ /INT/) {
+ if ($verbose) {
+ print "$0: Sending SIGINT signal to process $pid\n";
+ }
+ kill 'INT', $pid;
+ }
+
+ # Send SIGQUIT (whenever we need graceful termination)
+ elsif ($line =~ /QUIT/) {
+ if ($verbose) {
+ print "$0: Sending SIGQUIT signal to process $pid\n";
+ }
+ kill 'QUIT', $pid;
+ }
+
+ # Send SIGKILL
+ elsif ($line =~ /KILL/) {
+ if ($verbose) {
+ print "$0: Sending SIGKILL signal to process $pid\n";
+ }
+ kill 'KILL', $pid;
+ }
+
+ # Close pipe (sends EOF notification to child)
+ elsif ($line =~ /CLOSE/) {
+ if ($verbose) {
+ print "$0: Closing output end of pipe to child $pid\n";
+ }
+ close Writer;
+ }
+
+ # Wait for child to terminate
+ elsif ($line =~ /WAIT/) {
+ if ($verbose) {
+ print "$0: Waiting for child $pid\n";
+ }
+ wait;
+ if ($verbose) {
+ print "$0: Child $pid reaped\n";
+ }
+ }
+
+ # Sleep
+ elsif ($line =~ /SLEEP (\d+)/) {
+ if ($verbose) {
+ print "$0: Sleeping $1 secs\n";
+ }
+ sleep $1;
+ }
+
+ # Unknown input
+ else {
+ if ($verbose) {
+ print "$0: Sending :$line: to child $pid\n";
+ }
+ print Writer "$line\n";
+ }
+}
+
+#
+# Parent echoes the output produced by the child.
+#
+close Writer;
+if ($verbose) {
+ print "$0: Reading data from child $pid\n";
+}
+while ($line = <Reader>) {
+ print $line;
+}
+close Reader;
+
+# Finally, parent reaps child
+wait;
+
+if ($verbose) {
+ print "$0: Shell terminated\n";
+}
+
+exit;
diff --git a/trace01.txt b/trace01.txt
new file mode 100644
index 0000000..9c8561e
--- /dev/null
+++ b/trace01.txt
@@ -0,0 +1,5 @@
+#
+# trace01.txt - Properly terminate on EOF.
+#
+CLOSE
+WAIT
diff --git a/trace02.txt b/trace02.txt
new file mode 100644
index 0000000..e97643b
--- /dev/null
+++ b/trace02.txt
@@ -0,0 +1,5 @@
+#
+# trace02.txt - Process builtin quit command.
+#
+quit
+WAIT
diff --git a/trace03.txt b/trace03.txt
new file mode 100644
index 0000000..be8869c
--- /dev/null
+++ b/trace03.txt
@@ -0,0 +1,5 @@
+#
+# trace03.txt - Run a foreground job.
+#
+/bin/echo tsh> quit
+quit
diff --git a/trace04.txt b/trace04.txt
new file mode 100644
index 0000000..554df0e
--- /dev/null
+++ b/trace04.txt
@@ -0,0 +1,5 @@
+#
+# trace04.txt - Run a background job.
+#
+/bin/echo -e tsh> ./myspin 1 \046
+./myspin 1 &
diff --git a/trace05.txt b/trace05.txt
new file mode 100644
index 0000000..03c1876
--- /dev/null
+++ b/trace05.txt
@@ -0,0 +1,11 @@
+#
+# trace05.txt - Process jobs builtin command.
+#
+/bin/echo -e tsh> ./myspin 2 \046
+./myspin 2 &
+
+/bin/echo -e tsh> ./myspin 3 \046
+./myspin 3 &
+
+/bin/echo tsh> jobs
+jobs
diff --git a/trace06.txt b/trace06.txt
new file mode 100644
index 0000000..bfa3890
--- /dev/null
+++ b/trace06.txt
@@ -0,0 +1,8 @@
+#
+# trace06.txt - Forward SIGINT to foreground job.
+#
+/bin/echo -e tsh> ./myspin 4
+./myspin 4
+
+SLEEP 2
+INT
diff --git a/trace07.txt b/trace07.txt
new file mode 100644
index 0000000..259a285
--- /dev/null
+++ b/trace07.txt
@@ -0,0 +1,14 @@
+#
+# trace07.txt - Forward SIGINT only to foreground job.
+#
+/bin/echo -e tsh> ./myspin 4 \046
+./myspin 4 &
+
+/bin/echo -e tsh> ./myspin 5
+./myspin 5
+
+SLEEP 2
+INT
+
+/bin/echo tsh> jobs
+jobs
diff --git a/trace08.txt b/trace08.txt
new file mode 100644
index 0000000..49be19f
--- /dev/null
+++ b/trace08.txt
@@ -0,0 +1,14 @@
+#
+# trace08.txt - Forward SIGTSTP only to foreground job.
+#
+/bin/echo -e tsh> ./myspin 4 \046
+./myspin 4 &
+
+/bin/echo -e tsh> ./myspin 5
+./myspin 5
+
+SLEEP 2
+TSTP
+
+/bin/echo tsh> jobs
+jobs
diff --git a/trace09.txt b/trace09.txt
new file mode 100644
index 0000000..340d998
--- /dev/null
+++ b/trace09.txt
@@ -0,0 +1,20 @@
+#
+# trace09.txt - Process bg builtin command
+#
+/bin/echo -e tsh> ./myspin 4 \046
+./myspin 4 &
+
+/bin/echo -e tsh> ./myspin 5
+./myspin 5
+
+SLEEP 2
+TSTP
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> bg %2
+bg %2
+
+/bin/echo tsh> jobs
+jobs
diff --git a/trace10.txt b/trace10.txt
new file mode 100644
index 0000000..e7de996
--- /dev/null
+++ b/trace10.txt
@@ -0,0 +1,22 @@
+#
+# trace10.txt - Process fg builtin command.
+#
+/bin/echo -e tsh> ./myspin 4 \046
+./myspin 4 &
+
+SLEEP 1
+/bin/echo tsh> fg %1
+fg %1
+
+SLEEP 1
+TSTP
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> fg %1
+fg %1
+
+/bin/echo tsh> jobs
+jobs
+
diff --git a/trace11.txt b/trace11.txt
new file mode 100644
index 0000000..d2f8663
--- /dev/null
+++ b/trace11.txt
@@ -0,0 +1,12 @@
+#
+# trace11.txt - Forward SIGINT to every process in foreground process group
+#
+/bin/echo -e tsh> ./mysplit 4
+./mysplit 4
+
+SLEEP 2
+INT
+
+/bin/echo tsh> /bin/ps a
+/bin/ps a
+
diff --git a/trace12.txt b/trace12.txt
new file mode 100644
index 0000000..88455e3
--- /dev/null
+++ b/trace12.txt
@@ -0,0 +1,17 @@
+#
+# trace12.txt - Forward SIGTSTP to every process in foreground process group
+#
+/bin/echo -e tsh> ./mysplit 4
+./mysplit 4
+
+SLEEP 2
+TSTP
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> /bin/ps a
+/bin/ps a
+
+
+
diff --git a/trace13.txt b/trace13.txt
new file mode 100644
index 0000000..d734cbc
--- /dev/null
+++ b/trace13.txt
@@ -0,0 +1,23 @@
+#
+# trace13.txt - Restart every stopped process in process group
+#
+/bin/echo -e tsh> ./mysplit 4
+./mysplit 4
+
+SLEEP 2
+TSTP
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> /bin/ps a
+/bin/ps a
+
+/bin/echo tsh> fg %1
+fg %1
+
+/bin/echo tsh> /bin/ps a
+/bin/ps a
+
+
+
diff --git a/trace14.txt b/trace14.txt
new file mode 100644
index 0000000..8086580
--- /dev/null
+++ b/trace14.txt
@@ -0,0 +1,47 @@
+#
+# trace14.txt - Simple error handling
+#
+/bin/echo tsh> ./bogus
+./bogus
+
+/bin/echo -e tsh> ./myspin 4 \046
+./myspin 4 &
+
+/bin/echo tsh> fg
+fg
+
+/bin/echo tsh> bg
+bg
+
+/bin/echo tsh> fg a
+fg a
+
+/bin/echo tsh> bg a
+bg a
+
+/bin/echo tsh> fg 9999999
+fg 9999999
+
+/bin/echo tsh> bg 9999999
+bg 9999999
+
+/bin/echo tsh> fg %2
+fg %2
+
+/bin/echo tsh> fg %1
+fg %1
+
+SLEEP 2
+TSTP
+
+/bin/echo tsh> bg %2
+bg %2
+
+/bin/echo tsh> bg %1
+bg %1
+
+/bin/echo tsh> jobs
+jobs
+
+
+
diff --git a/trace15.txt b/trace15.txt
new file mode 100644
index 0000000..2cc780e
--- /dev/null
+++ b/trace15.txt
@@ -0,0 +1,46 @@
+#
+# trace15.txt - Putting it all together
+#
+
+/bin/echo tsh> ./bogus
+./bogus
+
+/bin/echo tsh> ./myspin 10
+./myspin 10
+
+SLEEP 2
+INT
+
+/bin/echo -e tsh> ./myspin 3 \046
+./myspin 3 &
+
+/bin/echo -e tsh> ./myspin 4 \046
+./myspin 4 &
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> fg %1
+fg %1
+
+SLEEP 2
+TSTP
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> bg %3
+bg %3
+
+/bin/echo tsh> bg %1
+bg %1
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> fg %1
+fg %1
+
+/bin/echo tsh> quit
+quit
+
diff --git a/trace16.txt b/trace16.txt
new file mode 100644
index 0000000..822ba14
--- /dev/null
+++ b/trace16.txt
@@ -0,0 +1,16 @@
+#
+# trace16.txt - Tests whether the shell can handle SIGTSTP and SIGINT
+# signals that come from other processes instead of the terminal.
+#
+
+/bin/echo tsh> ./mystop 2
+./mystop 2
+
+SLEEP 3
+
+/bin/echo tsh> jobs
+jobs
+
+/bin/echo tsh> ./myint 2
+./myint 2
+
diff --git a/tsh.c b/tsh.c
new file mode 100644
index 0000000..7c2ea8d
--- /dev/null
+++ b/tsh.c
@@ -0,0 +1,509 @@
+/*
+ * tsh - A tiny shell program with job control
+ *
+ * <Put your name and login ID here>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+/* Misc manifest constants */
+#define MAXLINE 1024 /* max line size */
+#define MAXARGS 128 /* max args on a command line */
+#define MAXJOBS 16 /* max jobs at any point in time */
+#define MAXJID 1<<16 /* max job ID */
+
+/* Job states */
+#define UNDEF 0 /* undefined */
+#define FG 1 /* running in foreground */
+#define BG 2 /* running in background */
+#define ST 3 /* stopped */
+
+/*
+ * Jobs states: FG (foreground), BG (background), ST (stopped)
+ * Job state transitions and enabling actions:
+ * FG -> ST : ctrl-z
+ * ST -> FG : fg command
+ * ST -> BG : bg command
+ * BG -> FG : fg command
+ * At most 1 job can be in the FG state.
+ */
+
+/* Global variables */
+extern char **environ; /* defined in libc */
+char prompt[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */
+int verbose = 0; /* if true, print additional output */
+int nextjid = 1; /* next job ID to allocate */
+char sbuf[MAXLINE]; /* for composing sprintf messages */
+
+struct job_t { /* The job struct */
+ pid_t pid; /* job PID */
+ int jid; /* job ID [1, 2, ...] */
+ int state; /* UNDEF, BG, FG, or ST */
+ char cmdline[MAXLINE]; /* command line */
+};
+struct job_t jobs[MAXJOBS]; /* The job list */
+/* End global variables */
+
+
+/* Function prototypes */
+
+/* Here are the functions that you will implement */
+void eval(char *cmdline);
+int builtin_cmd(char **argv);
+void do_bgfg(char **argv);
+void waitfg(pid_t pid);
+
+void sigchld_handler(int sig);
+void sigtstp_handler(int sig);
+void sigint_handler(int sig);
+
+/* Here are helper routines that we've provided for you */
+int parseline(const char *cmdline, char **argv);
+void sigquit_handler(int sig);
+
+void clearjob(struct job_t *job);
+void initjobs(struct job_t *jobs);
+int maxjid(struct job_t *jobs);
+int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);
+int deletejob(struct job_t *jobs, pid_t pid);
+pid_t fgpid(struct job_t *jobs);
+struct job_t *getjobpid(struct job_t *jobs, pid_t pid);
+struct job_t *getjobjid(struct job_t *jobs, int jid);
+int pid2jid(pid_t pid);
+void listjobs(struct job_t *jobs);
+
+void usage(void);
+void unix_error(char *msg);
+void app_error(char *msg);
+typedef void handler_t(int);
+handler_t *Signal(int signum, handler_t *handler);
+
+/*
+ * main - The shell's main routine
+ */
+int main(int argc, char **argv)
+{
+ char c;
+ char cmdline[MAXLINE];
+ int emit_prompt = 1; /* emit prompt (default) */
+
+ /* Redirect stderr to stdout (so that driver will get all output
+ * on the pipe connected to stdout) */
+ dup2(1, 2);
+
+ /* Parse the command line */
+ while ((c = getopt(argc, argv, "hvp")) != EOF) {
+ switch (c) {
+ case 'h': /* print help message */
+ usage();
+ break;
+ case 'v': /* emit additional diagnostic info */
+ verbose = 1;
+ break;
+ case 'p': /* don't print a prompt */
+ emit_prompt = 0; /* handy for automatic testing */
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* Install the signal handlers */
+
+ /* These are the ones you will need to implement */
+ Signal(SIGINT, sigint_handler); /* ctrl-c */
+ Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
+ Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
+
+ /* This one provides a clean way to kill the shell */
+ Signal(SIGQUIT, sigquit_handler);
+
+ /* Initialize the job list */
+ initjobs(jobs);
+
+ /* Execute the shell's read/eval loop */
+ while (1) {
+
+ /* Read command line */
+ if (emit_prompt) {
+ printf("%s", prompt);
+ fflush(stdout);
+ }
+ if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
+ app_error("fgets error");
+ if (feof(stdin)) { /* End of file (ctrl-d) */
+ fflush(stdout);
+ exit(0);
+ }
+
+ /* Evaluate the command line */
+ eval(cmdline);
+ fflush(stdout);
+ fflush(stdout);
+ }
+
+ exit(0); /* control never reaches here */
+}
+
+/*
+ * eval - Evaluate the command line that the user has just typed in
+ *
+ * If the user has requested a built-in command (quit, jobs, bg or fg)
+ * then execute it immediately. Otherwise, fork a child process and
+ * run the job in the context of the child. If the job is running in
+ * the foreground, wait for it to terminate and then return. Note:
+ * each child process must have a unique process group ID so that our
+ * background children don't receive SIGINT (SIGTSTP) from the kernel
+ * when we type ctrl-c (ctrl-z) at the keyboard.
+*/
+void eval(char *cmdline)
+{
+ return;
+}
+
+/*
+ * parseline - Parse the command line and build the argv array.
+ *
+ * Characters enclosed in single quotes are treated as a single
+ * argument. Return true if the user has requested a BG job, false if
+ * the user has requested a FG job.
+ */
+int parseline(const char *cmdline, char **argv)
+{
+ static char array[MAXLINE]; /* holds local copy of command line */
+ char *buf = array; /* ptr that traverses command line */
+ char *delim; /* points to first space delimiter */
+ int argc; /* number of args */
+ int bg; /* background job? */
+
+ strcpy(buf, cmdline);
+ buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */
+ while (*buf && (*buf == ' ')) /* ignore leading spaces */
+ buf++;
+
+ /* Build the argv list */
+ argc = 0;
+ if (*buf == '\'') {
+ buf++;
+ delim = strchr(buf, '\'');
+ }
+ else {
+ delim = strchr(buf, ' ');
+ }
+
+ while (delim) {
+ argv[argc++] = buf;
+ *delim = '\0';
+ buf = delim + 1;
+ while (*buf && (*buf == ' ')) /* ignore spaces */
+ buf++;
+
+ if (*buf == '\'') {
+ buf++;
+ delim = strchr(buf, '\'');
+ }
+ else {
+ delim = strchr(buf, ' ');
+ }
+ }
+ argv[argc] = NULL;
+
+ if (argc == 0) /* ignore blank line */
+ return 1;
+
+ /* should the job run in the background? */
+ if ((bg = (*argv[argc-1] == '&')) != 0) {
+ argv[--argc] = NULL;
+ }
+ return bg;
+}
+
+/*
+ * builtin_cmd - If the user has typed a built-in command then execute
+ * it immediately.
+ */
+int builtin_cmd(char **argv)
+{
+ return 0; /* not a builtin command */
+}
+
+/*
+ * do_bgfg - Execute the builtin bg and fg commands
+ */
+void do_bgfg(char **argv)
+{
+ return;
+}
+
+/*
+ * waitfg - Block until process pid is no longer the foreground process
+ */
+void waitfg(pid_t pid)
+{
+ return;
+}
+
+/*****************
+ * Signal handlers
+ *****************/
+
+/*
+ * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
+ * a child job terminates (becomes a zombie), or stops because it
+ * received a SIGSTOP or SIGTSTP signal. The handler reaps all
+ * available zombie children, but doesn't wait for any other
+ * currently running children to terminate.
+ */
+void sigchld_handler(int sig)
+{
+ return;
+}
+
+/*
+ * sigint_handler - The kernel sends a SIGINT to the shell whenver the
+ * user types ctrl-c at the keyboard. Catch it and send it along
+ * to the foreground job.
+ */
+void sigint_handler(int sig)
+{
+ return;
+}
+
+/*
+ * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
+ * the user types ctrl-z at the keyboard. Catch it and suspend the
+ * foreground job by sending it a SIGTSTP.
+ */
+void sigtstp_handler(int sig)
+{
+ return;
+}
+
+/*********************
+ * End signal handlers
+ *********************/
+
+/***********************************************
+ * Helper routines that manipulate the job list
+ **********************************************/
+
+/* clearjob - Clear the entries in a job struct */
+void clearjob(struct job_t *job) {
+ job->pid = 0;
+ job->jid = 0;
+ job->state = UNDEF;
+ job->cmdline[0] = '\0';
+}
+
+/* initjobs - Initialize the job list */
+void initjobs(struct job_t *jobs) {
+ int i;
+
+ for (i = 0; i < MAXJOBS; i++)
+ clearjob(&jobs[i]);
+}
+
+/* maxjid - Returns largest allocated job ID */
+int maxjid(struct job_t *jobs)
+{
+ int i, max=0;
+
+ for (i = 0; i < MAXJOBS; i++)
+ if (jobs[i].jid > max)
+ max = jobs[i].jid;
+ return max;
+}
+
+/* addjob - Add a job to the job list */
+int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
+{
+ int i;
+
+ if (pid < 1)
+ return 0;
+
+ for (i = 0; i < MAXJOBS; i++) {
+ if (jobs[i].pid == 0) {
+ jobs[i].pid = pid;
+ jobs[i].state = state;
+ jobs[i].jid = nextjid++;
+ if (nextjid > MAXJOBS)
+ nextjid = 1;
+ strcpy(jobs[i].cmdline, cmdline);
+ if(verbose){
+ printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
+ }
+ return 1;
+ }
+ }
+ printf("Tried to create too many jobs\n");
+ return 0;
+}
+
+/* deletejob - Delete a job whose PID=pid from the job list */
+int deletejob(struct job_t *jobs, pid_t pid)
+{
+ int i;
+
+ if (pid < 1)
+ return 0;
+
+ for (i = 0; i < MAXJOBS; i++) {
+ if (jobs[i].pid == pid) {
+ clearjob(&jobs[i]);
+ nextjid = maxjid(jobs)+1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* fgpid - Return PID of current foreground job, 0 if no such job */
+pid_t fgpid(struct job_t *jobs) {
+ int i;
+
+ for (i = 0; i < MAXJOBS; i++)
+ if (jobs[i].state == FG)
+ return jobs[i].pid;
+ return 0;
+}
+
+/* getjobpid - Find a job (by PID) on the job list */
+struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
+ int i;
+
+ if (pid < 1)
+ return NULL;
+ for (i = 0; i < MAXJOBS; i++)
+ if (jobs[i].pid == pid)
+ return &jobs[i];
+ return NULL;
+}
+
+/* getjobjid - Find a job (by JID) on the job list */
+struct job_t *getjobjid(struct job_t *jobs, int jid)
+{
+ int i;
+
+ if (jid < 1)
+ return NULL;
+ for (i = 0; i < MAXJOBS; i++)
+ if (jobs[i].jid == jid)
+ return &jobs[i];
+ return NULL;
+}
+
+/* pid2jid - Map process ID to job ID */
+int pid2jid(pid_t pid)
+{
+ int i;
+
+ if (pid < 1)
+ return 0;
+ for (i = 0; i < MAXJOBS; i++)
+ if (jobs[i].pid == pid) {
+ return jobs[i].jid;
+ }
+ return 0;
+}
+
+/* listjobs - Print the job list */
+void listjobs(struct job_t *jobs)
+{
+ int i;
+
+ for (i = 0; i < MAXJOBS; i++) {
+ if (jobs[i].pid != 0) {
+ printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
+ switch (jobs[i].state) {
+ case BG:
+ printf("Running ");
+ break;
+ case FG:
+ printf("Foreground ");
+ break;
+ case ST:
+ printf("Stopped ");
+ break;
+ default:
+ printf("listjobs: Internal error: job[%d].state=%d ",
+ i, jobs[i].state);
+ }
+ printf("%s", jobs[i].cmdline);
+ }
+ }
+}
+/******************************
+ * end job list helper routines
+ ******************************/
+
+
+/***********************
+ * Other helper routines
+ ***********************/
+
+/*
+ * usage - print a help message
+ */
+void usage(void)
+{
+ printf("Usage: shell [-hvp]\n");
+ printf(" -h print this message\n");
+ printf(" -v print additional diagnostic information\n");
+ printf(" -p do not emit a command prompt\n");
+ exit(1);
+}
+
+/*
+ * unix_error - unix-style error routine
+ */
+void unix_error(char *msg)
+{
+ fprintf(stdout, "%s: %s\n", msg, strerror(errno));
+ exit(1);
+}
+
+/*
+ * app_error - application-style error routine
+ */
+void app_error(char *msg)
+{
+ fprintf(stdout, "%s\n", msg);
+ exit(1);
+}
+
+/*
+ * Signal - wrapper for the sigaction function
+ */
+handler_t *Signal(int signum, handler_t *handler)
+{
+ struct sigaction action, old_action;
+
+ action.sa_handler = handler;
+ sigemptyset(&action.sa_mask); /* block sigs of type being handled */
+ action.sa_flags = SA_RESTART; /* restart syscalls if possible */
+
+ if (sigaction(signum, &action, &old_action) < 0)
+ unix_error("Signal error");
+ return (old_action.sa_handler);
+}
+
+/*
+ * sigquit_handler - The driver program can gracefully terminate the
+ * child shell by sending it a SIGQUIT signal.
+ */
+void sigquit_handler(int sig)
+{
+ printf("Terminating after receipt of SIGQUIT signal\n");
+ exit(1);
+}
+
+
+
diff --git a/tshref b/tshref
new file mode 100755
index 0000000..d7e2108
--- /dev/null
+++ b/tshref
Binary files differ