Question: 1. Modify this project so that you can use 'ls' instead of '/bin/ls' * The command starts with a dot, such as ./shell * The

1. Modify this project so that you can use 'ls' instead of '/bin/ls'
* The command starts with a dot, such as ./shell
* The command is somewhere in the ENVPATH, such as l
Test: ./shell -test path
2. Modify this project so that the command prompt includes a counter that
increments for each command executed (starting with 1). Your
program should use the following prompt format:
"Shell(pid=%1)%2> " %1=process pid %2=counter
Test: ./shell -test counter
3. Modify this project so that '!NN' re-executes the n'th command entered.
no more than 9 values will be entered. If it goes over 9, loop back to 1 (use a circular queue structure)
Shell(...)1> ls
Shell(...)2> !1 # re-executes ls
Shell(...)3> !2 # re-executes ls
Test: ./shell -test rerun
4. Modify the project so that it uses waitpid instead of wait.
The parent process must wait for its direct child to finish executing before showing the next shell prompt.
5. Create a new builtin command 'sub' that forks the program to create
a new subshell. The parent shell should run the imtheparent()
function just as if we were running an external command (like 'ls').
./shell
Shell(.n1..)1> newsub
Shell(.n2..)1> exit # Exits sub shell
Shell(.n1..)1> exit # Exits back to 'real' shell
6. Create a new global variable to prevent a subshell from invoking
a subshell invoking a subshell (i.e., more than 3 levels deep):
./shell
Shell(.n1..)1> newsub
Shell(.n2..)1> newsub
Shell(.n3..)1> newsub # prints "Too deep!" to stderr

// This is the shell.c

Test: ./shell -test sub

#include /* Character types */

#include /* Standard buffered input/output */

#include /* Standard library functions */

#include /* String operations */

#include /* Data types */

#include /* Declarations for waiting */

#include /* 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;

}

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' ",

child_pid);

if (run_in_background) {

fprintf(stderr,

" Parent says 'run_in_background=1 ... so we're not waiting for the child' ");

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.' ",

child_pid);

if (child_error_code != 0) {

fprintf(stderr,

" Parent says 'Child process %d failed with code %d' ",

child_pid, child_error_code);

}

}

int get_shell_command(char *buffer, int size)

{

int i = 0;

char c;

while (i < size - 1 && (c = getchar()) != EOF && c != ' ') {

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;

}

/* 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;

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);

run_in_background = n_read > 2 && buffer[n_read - 2] == '&';

buffer[n_read - run_in_background - 1] = ' ';

// 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] != ' ') && (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)

continue;

exec_argv[exec_argc] = NULL;

if (!strcmp(exec_argv[0], "exit"))

{

printf("Exiting process %d ", 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 %s ", exec_argv[1]);

}

else

{

pid_from_fork = fork();

if (pid_from_fork < 0)

{

fprintf(stderr, "fork failed ");

continue;

}

if (pid_from_fork == 0)

{

// TO-DO P5.6

return imthechild(exec_argv[0], &exec_argv[0]);

/* 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

#undef _GNU_SOURCE

#include

#include

#include

#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)

{

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("ls /bin/ls exit ", 1, args);

/* Check output */

err = fopen("smp1.err", "r");

quit_if(fscanf(err, " Parent says 'child process has been forked with pid=%d' "

" Parent says 'wait() returned so the child with pid=%d is finished.' "

" Parent says 'child process has been forked with pid=%d' "

" Parent says 'wait() returned so the child with pid=%d is finished.' ",

&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(" /bin/true exit ", 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 %d ", &pid_tmp, &pid_tmp, &pid_tmp, &pid_tmp) != 4);

quit_if(!feof(out));

fclose(out);

return EXIT_SUCCESS;

}

/* 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 test !1 exit ", 1, args);

/* Check output */

out = fopen("smp1.out", "r");

quit_if(fscanf(out, "Shell(pid=%d)1> test Shell(pid=%d)2> test Shell(pid=%d)3> Exiting process %d ", &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("newsub newsub newsub exit exit exit ", 1, args);

/* 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! ", 10))

warned_too_deep = 1;

}

quit_if(!warned_too_deep);

fclose(out);

fclose(err);

return EXIT_SUCCESS;

}

/*

* Main entry point for SMP1 test harness

*/

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;

}

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!