-- This is the shell-c Test- --shell -test sub #include -ctype-h- -- C.pdf
1. // This is the shell.c
Test: ./shell -test sub
#include <ctype.h> /* Character types */
#include <stdio.h> /* Standard buffered input/output */
#include <stdlib.h> /* Standard library functions */
#include <string.h> /* String operations */
#include <sys/types.h> /* Data types */
#include <sys/wait.h> /* Declarations for waiting */
#include <unistd.h> /* Standard symbolic constants and types */
#include "smp1_tests.h" /* Built-in test system */
/* DEFINE SECTION */
#define SHELL_BUFFER_SIZE 256 /* Size of the Shell input buffer */
#define SHELL_MAX_ARGS 8 /* Maximum number of arguments parsed */
#define SHELL_HISTORY_SIZE 10
/* VARIABLE SECTION */
enum { STATE_SPACE, STATE_NON_SPACE }; /* Parser states */
char shell_history[SHELL_HISTORY_SIZE][SHELL_BUFFER_SIZE];
int shell_history_next_index = 0;
int imthechild(const char *path_to_exec, char *const args[])
{
// TO-DO P5.1
return execvp(path_to_exec, args) ? -1 : 0;
}
2. void imtheparent(pid_t child_pid, int run_in_background)
{
int child_return_val, child_error_code;
/* fork returned a positive pid so we are the parent */
fprintf(stderr,
" Parent says 'child process has been forked with pid=%d'n",
child_pid);
if (run_in_background) {
fprintf(stderr,
" Parent says 'run_in_background=1 ... so we're not waiting for the child'n");
return;
}
// TO-DO P5.4
wait(&child_return_val);
/* Use the WEXITSTATUS to extract the status code from the return value */
child_error_code = WEXITSTATUS(child_return_val);
fprintf(stderr,
" Parent says 'wait() returned so the child with pid=%d is finished.'n",
child_pid);
if (child_error_code != 0) {
fprintf(stderr,
" Parent says 'Child process %d failed with code %d'n",
child_pid, child_error_code);
3. }
}
int get_shell_command(char *buffer, int size)
{
int i = 0;
char c;
while (i < size - 1 && (c = getchar()) != EOF && c != 'n') {
buffer[i++] = c;
}
buffer[i] = '0';
return i;
}
void parse_command(char *command, char **exec_args, int *exec_bg)
{
int argc = 0;
*exec_bg = 0;
while (*command != '0') {
/* Strip whitespace. */
while (isspace(*command)) {
++command;
}
if (*command == '0') {
break;
4. }
/* Save the argument. */
if (argc < SHELL_MAX_ARGS) {
exec_args[argc++] = command;
}
while (*command != '0' && !isspace(*command)) {
++command;
}
if (*command != '0') {
*command++ = '0';
}
}
exec_args[argc] = NULL;
/* Check for background execution request. */
if (argc > 0 && !strcmp(exec_args[argc - 1], "&")) {
*exec_bg = 1;
exec_args[--argc] = NULL;
}
}
void run_shell_command;
int main(int argc, char **argv)
{
pid_t shell_pid, pid_from_fork;
5. int n_read, i, exec_argc, parser_state, run_in_background;
char buffer[SHELL_BUFFER_SIZE];
char *exec_argv[SHELL_MAX_ARGS + 1];
// TO-DO new variables for P5.2, P5.3, P5.6
if (argc > 1 && !strcmp(argv[1], "-test"))
{
return run_smp1_tests(argc - 1, argv + 1);
}
shell_pid = getpid();
int command_counter = 1;
while (1)
{
/* The Shell runs in an infinite loop, processing input. */
if (exec_argc > 0)
{
command_counter++;
}
// TO-DO P5.2
fprintf(stdout, "Shell(pid=%d)%d> ", shell_pid, command_counter);
fflush(stdout);
if (fgets(buffer, SHELL_BUFFER_SIZE, stdin) == NULL)
return EXIT_SUCCESS;
n_read = strlen(buffer);
6. run_in_background = n_read > 2 && buffer[n_read - 2] == '&';
buffer[n_read - run_in_background - 1] = 'n';
// TO-DO P5.3
/* Parse the arguments: the first argument is the file or command *
* we want to run. */
parser_state = STATE_SPACE;
for (exec_argc = 0, i = 0;
(buffer[i] != 'n') && (exec_argc < SHELL_MAX_ARGS); i++)
{
if (!isspace(buffer[i]))
{
if (parser_state == STATE_SPACE)
exec_argv[exec_argc++] = &buffer[i];
parser_state = STATE_NON_SPACE;
}
else
{
buffer[i] = '0';
parser_state = STATE_SPACE;
}
}
buffer[i] = '0'; /* Terminate input, overwriting the '&' if it exists */
if (!exec_argc)
7. continue;
exec_argv[exec_argc] = NULL;
if (!strcmp(exec_argv[0], "exit"))
{
printf("Exiting process %dn", shell_pid);
return EXIT_SUCCESS; /* End Shell program */
} else if (!strcmp(exec_argv[0], "cd") && exec_argc > 1)
{
if (chdir(exec_argv[1]))
fprintf(stderr, "cd: failed to chdir %sn", exec_argv[1]);
}
else
{
pid_from_fork = fork();
if (pid_from_fork < 0)
{
fprintf(stderr, "fork failedn");
continue;
}
if (pid_from_fork == 0)
{
// TO-DO P5.6
return imthechild(exec_argv[0], &exec_argv[0]);
8. /* Exit from main. */
}
else
{
imtheparent(pid_from_fork, run_in_background);
}
}
}
return EXIT_SUCCESS;
}
/* end main() */
//This is the smp1._tests.c
#define _GNU_SOURCE
#include <stdio.h>
#undef _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "testrunner.h"
#include "smp1_tests.h"
#define quit_if(cond) do {if (cond) exit(EXIT_FAILURE);} while(0)
/* Prepare input, reroute file descriptors, and run the program. */
void run_test(const char *input, int argc, char **argv)
9. {
FILE *in = fopen("smp1.in", "w");
fprintf(in, input);
fclose(in);
freopen("smp1.in", "r", stdin );
freopen("smp1.out", "w", stdout);
freopen("smp1.err", "w", stderr);
/* Run the program */
quit_if(main(argc, argv) != EXIT_SUCCESS);
fclose(stdout);
fclose(stderr);
}
/* P5.1: Test of executing commands in the path */
int test_path(int argc, char **argv)
{
char *args[] = { "./shell", NULL };
FILE *out, *err;
int pid_tmp;
/* Run the test */
run_test("lsn/bin/lsnexitn", 1, args);
/* Check output */
err = fopen("smp1.err", "r");
quit_if(fscanf(err, " Parent says 'child process has been forked with pid=%d'n"
10. " Parent says 'wait() returned so the child with pid=%d is finished.'n"
" Parent says 'child process has been forked with pid=%d'n"
" Parent says 'wait() returned so the child with pid=%d is finished.'n",
&pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);
quit_if(!feof(err));
fclose(err);
return EXIT_SUCCESS;
}
/* P5.2: Test of command line counter */
int test_counter(int argc, char **argv)
{
char *args[] = { "./shell", NULL };
FILE *out, *err;
int pid_tmp;
/* Run the test */
run_test("n/bin/truenexitn", 1, args);
/* Check output */
out = fopen("smp1.out", "r");
quit_if(fscanf(out, "Shell(pid=%d)1> Shell(pid=%d)1> Shell(pid=%d)2> Exiting process %dn",
&pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);
quit_if(!feof(out));
fclose(out);
return EXIT_SUCCESS;
}
11. /* P5.3: Test of re-executing earlier commands */
int test_rerun(int argc, char **argv)
{
char *args[] = { "./shell", NULL };
FILE *out, *err;
int pid_tmp;
/* Run the test */
run_test("/bin/echo testn!1nexitn", 1, args);
/* Check output */
out = fopen("smp1.out", "r");
quit_if(fscanf(out, "Shell(pid=%d)1> testnShell(pid=%d)2> testnShell(pid=%d)3> Exiting
process %dn", &pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);
quit_if(!feof(out));
fclose(out);
return EXIT_SUCCESS;
}
/* P5.5: Test of depth-limited sub */
int test_sub(int argc, char **argv)
{
char *args[] = { "./shell", NULL };
FILE *out, *err;
int pids[4], warned_too_deep;
/* Run the test */
run_test("newsubnnewsubnnewsubnexitnexitnexitn", 1, args);
12. /* Check output */
out = fopen("smp1.out", "r");
err = fopen("smp1.err", "r");
/* First, check that the subshells were invoked. */
fscanf(out, "Shell(pid=%d)1> Shell(pid=%d)1> Shell(pid=%d)1> Shell(pid=%d)2> ", &pids[0],
&pids[1], &pids[2], &pids[3]);
quit_if(!((pids[0] != pids[1]) && (pids[1] != pids[2]) && (pids[0] != pids[2]) && (pids[2] ==
pids[3])));
/* Next, check for the "Too deep!" message: */
warned_too_deep = 0;
/* Use a while loop because multiple processes write to stderr concurrently. */
while (!warned_too_deep && !feof(err)) {
char too_deep[11];
fgets(too_deep, 11, err);
if (!strncmp(too_deep, "Too deep!n", 10))
warned_too_deep = 1;
}
quit_if(!warned_too_deep);
fclose(out);
fclose(err);
return EXIT_SUCCESS;
}
/*
* Main entry point for SMP1 test harness
13. */
int run_smp1_tests(int argc, char **argv)
{
/* Tests can be invoked by matching their name or their suite name
or 'all' */
testentry_t tests[] = {
{ "newsub", "smp1", test_sub },
{ "rerun", "smp1", test_rerun },
{ "counter", "smp1", test_counter },
{ "path", "smp1", test_path } };
int result = run_testrunner(argc, argv, tests, sizeof(tests) / sizeof (testentry_t));
unlink("smp1.in");
unlink("smp1.out");
unlink("smp1.err");
return result;
}
1. Modify this project so that you can use 'ls' instead of '/bin/ls'