Skip to content
Snippets Groups Projects
Commit 961f9256 authored by Andrei MIGA-PAPADOPOL's avatar Andrei MIGA-PAPADOPOL
Browse files

Merge branch 'homework' into 'master'

Homework

See merge request andrei.miga/assignment-mini-shell!1
parents 75dbf467 99c89419
No related branches found
No related tags found
No related merge requests found
Pipeline #51126 passed
......@@ -6,6 +6,9 @@
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cmd.h"
#include "utils.h"
......@@ -18,9 +21,14 @@
*/
static bool shell_cd(word_t *dir)
{
/* TODO: Execute cd. */
if (!dir || dir->next_word)
return false;
return 0;
char *dirname = get_word(dir);
if (chdir(dirname))
return false;
return true;
}
/**
......@@ -28,9 +36,116 @@ static bool shell_cd(word_t *dir)
*/
static int shell_exit(void)
{
/* TODO: Execute exit/quit. */
exit(0);
}
static void create_redirections(simple_command_t *s, int fd[])
{
int fdpipe[2] = { -1, -1 };
fd[0] = -1;
fd[1] = -1;
fd[2] = -1;
if (s->aux) {
fdpipe[READ] = ((int *)s->aux)[READ];
fdpipe[WRITE] = ((int *)s->aux)[WRITE];
free(s->aux);
s->aux = NULL;
}
if (s->in) {
// Input redirection overrides input pipe
if (fdpipe[READ] != -1) {
DIE(close(fdpipe[READ]), "close");
fdpipe[READ] = -1;
}
int fin = open(get_word(s->in), O_RDONLY);
DIE(fin == -1, "open");
fd[0] = fin;
} else {
fd[0] = fdpipe[READ];
}
if (s->out) {
// Output redirection overrides output pipe
if (fdpipe[WRITE] != -1) {
DIE(close(fdpipe[WRITE]), "close");
fdpipe[WRITE] = -1;
}
int flags = O_CREAT | O_WRONLY;
if (s->io_flags & IO_OUT_APPEND)
flags |= O_APPEND;
else
flags |= O_TRUNC;
int fout = open(get_word(s->out), flags, S_IRUSR | S_IWUSR);
DIE(fout == -1, "open");
fd[1] = fout;
} else {
fd[1] = fdpipe[WRITE];
}
if (s->err) {
// This handles &> correctly
if (s->out == s->err) {
fd[2] = fd[1];
} else {
int flags = O_CREAT | O_WRONLY;
if (s->io_flags & IO_ERR_APPEND)
flags |= O_APPEND;
else
flags |= O_TRUNC;
int ferr = open(get_word(s->err), flags, S_IRUSR | S_IWUSR);
DIE(ferr == -1, "open");
fd[2] = ferr;
}
}
}
static void replace_redirections(int fd[])
{
for (int i = 0; i < 3; i++)
if (fd[i] != -1)
DIE(dup2(fd[i], i) != i, "dup2");
}
return 0; /* TODO: Replace with actual exit code. */
static void close_redirections(int fd[])
{
if (fd[0] != -1)
DIE(close(fd[0]), "close");
if (fd[1] != -1 && fd[1] != fd[0])
DIE(close(fd[1]), "close");
if (fd[2] != -1 && fd[2] != fd[1] && fd[2] != fd[0])
DIE(close(fd[2]), "close");
}
static void expand_environment_vars(word_t *verb)
{
while (verb != NULL) {
if (verb->expand) {
char *value = getenv(verb->string);
if (!value) {
verb->string = "";
} else {
verb->string = strdup(value);
DIE(!verb->string, "strdup");
}
verb->expand = false;
}
verb = verb->next_part;
}
}
static word_t *check_environment_assignment(word_t *verb)
{
while (verb != NULL) {
if (strcmp(verb->string, "=") == 0)
return verb;
verb = verb->next_part;
}
return NULL;
}
/**
......@@ -39,23 +154,102 @@ static int shell_exit(void)
*/
static int parse_simple(simple_command_t *s, int level, command_t *father)
{
/* TODO: Sanity checks. */
word_t *verb = s->verb;
DIE(!verb || !verb->string, "verb");
expand_environment_vars(verb);
char *cmd_name = get_word(verb);
int redir[3];
/* TODO: If builtin command, execute the command. */
create_redirections(s, redir);
/* TODO: If variable assignment, execute the assignment and return
* the exit status.
*/
// Internal commands
if (strcmp(cmd_name, "cd") == 0) {
free(cmd_name);
int result = 0;
/* TODO: If external command:
* 1. Fork new process
* 2c. Perform redirections in child
* 3c. Load executable in child
* 2. Wait for child
* 3. Return exit status
*/
if (!shell_cd(s->params)) {
const char *message = "no such file or directory\n";
return 0; /* TODO: Replace with actual exit status. */
write(redir[2], message, strlen(message));
result = 1;
}
close_redirections(redir);
return result;
} else if (strcmp(cmd_name, "exit") == 0 || strcmp(cmd_name, "quit") == 0) {
free(cmd_name);
close_redirections(redir);
return shell_exit();
}
// Variable assignment
word_t *equal_sign = check_environment_assignment(verb);
if (equal_sign) {
free(cmd_name);
close_redirections(redir);
word_t *value_part = equal_sign->next_part;
const char *equal_str = equal_sign->string;
equal_sign->next_part = NULL;
equal_sign->string = "";
char *varname = get_word(verb);
char *value = get_word(value_part);
equal_sign->next_part = value_part;
equal_sign->string = equal_str;
int status = setenv(varname, value, 1);
free(varname);
free(value);
return status;
}
// External command
pid_t new_pid = fork();
DIE(new_pid == -1, "fork");
// Shell process waits
if (new_pid != 0) {
close_redirections(redir);
free(cmd_name);
int exit_code;
do {
waitpid(new_pid, &exit_code, 0);
} while (!WIFEXITED(exit_code) && !WIFSIGNALED(exit_code));
if (WIFEXITED(exit_code))
return WEXITSTATUS(exit_code);
return WTERMSIG(exit_code);
}
// Child process calls execvp
// Save the stderr descriptor, but mark the copy with CLOEXEC
// If exec succeeds, this copy is closed
// If it fails, it is needed to print the error message
int stderr_fd = dup(2);
DIE(stderr_fd == -1, "dup");
DIE(fcntl(stderr_fd, F_SETFD, FD_CLOEXEC), "fcntl");
replace_redirections(redir);
close_redirections(redir);
int size;
char **args = get_argv(s, &size);
execvp(cmd_name, args);
// Restore old stderr to print error message
DIE(dup2(stderr_fd, 2) != 2, "dup2");
DIE(close(stderr_fd), "close");
fprintf(stderr, "Execution failed for '%s'\n", cmd_name);
free(cmd_name);
exit(1);
}
/**
......@@ -64,9 +258,12 @@ static int parse_simple(simple_command_t *s, int level, command_t *father)
static bool run_in_parallel(command_t *cmd1, command_t *cmd2, int level,
command_t *father)
{
/* TODO: Execute cmd1 and cmd2 simultaneously. */
pid_t new_pid = fork();
return true; /* TODO: Replace with actual exit status. */
DIE(new_pid == -1, "fork");
if (new_pid == 0)
exit(parse_command(cmd1, level, father));
return parse_command(cmd2, level, father);
}
/**
......@@ -75,9 +272,30 @@ static bool run_in_parallel(command_t *cmd1, command_t *cmd2, int level,
static bool run_on_pipe(command_t *cmd1, command_t *cmd2, int level,
command_t *father)
{
/* TODO: Redirect the output of cmd1 to the input of cmd2. */
int *fd = malloc(2 * sizeof(int));
DIE(!fd, "malloc");
DIE(pipe(fd), "pipe");
pid_t new_pid = fork();
return true; /* TODO: Replace with actual exit status. */
DIE(new_pid == -1, "fork");
if (new_pid == 0) {
DIE(close(fd[READ]), "close");
if (father->aux)
fd[READ] = ((int *)father->aux)[READ];
else
fd[READ] = -1;
cmd1->aux = fd;
exit(parse_command(cmd1, level, father));
} else {
DIE(close(fd[WRITE]), "close");
if (father->aux)
fd[WRITE] = ((int *)father->aux)[WRITE];
else
fd[WRITE] = -1;
cmd2->aux = fd;
return parse_command(cmd2, level, father);
}
}
/**
......@@ -85,44 +303,43 @@ static bool run_on_pipe(command_t *cmd1, command_t *cmd2, int level,
*/
int parse_command(command_t *c, int level, command_t *father)
{
/* TODO: sanity checks */
if (c->op == OP_NONE) {
/* TODO: Execute a simple command. */
return 0; /* TODO: Replace with actual exit code of command. */
c->scmd->aux = c->aux;
return parse_simple(c->scmd, level, father);
}
switch (c->op) {
case OP_SEQUENTIAL:
/* TODO: Execute the commands one after the other. */
break;
parse_command(c->cmd1, level + 1, c);
return parse_command(c->cmd2, level + 1, c);
case OP_PARALLEL:
/* TODO: Execute the commands simultaneously. */
break;
return run_in_parallel(c->cmd1, c->cmd2, level + 1, c);
case OP_CONDITIONAL_NZERO:
/* TODO: Execute the second command only if the first one
* returns non zero.
*/
{
int ret1 = parse_command(c->cmd1, level + 1, c);
if (ret1)
return parse_command(c->cmd2, level + 1, c);
return ret1;
}
break;
case OP_CONDITIONAL_ZERO:
/* TODO: Execute the second command only if the first one
* returns zero.
*/
{
int ret1 = parse_command(c->cmd1, level + 1, c);
if (!ret1)
return parse_command(c->cmd2, level + 1, c);
return ret1;
}
break;
case OP_PIPE:
/* TODO: Redirect the output of the first command to the
* input of the second.
*/
break;
return run_on_pipe(c->cmd1, c->cmd2, level + 1, c);
default:
return SHELL_EXIT;
}
return 0; /* TODO: Replace with actual exit code of command. */
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment