diff --git a/Lab-2/.gitignore b/Lab-2/.gitignore new file mode 100644 index 0000000..9e4fe1b --- /dev/null +++ b/Lab-2/.gitignore @@ -0,0 +1,32 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app \ No newline at end of file diff --git a/Lab-2/Makefile b/Lab-2/Makefile new file mode 100644 index 0000000..af7695c --- /dev/null +++ b/Lab-2/Makefile @@ -0,0 +1,24 @@ +DEBUG=y + +CC=g++ +CFLAGS= -Werror -Wall +LDFLAGS= + +ifeq ($DEBUG), y) + CFLAGS += -DDEBUG -g +else + CFLAGS += -O2 +endif + +.PHONY: all clean + +all: bin/shell + +bin/shell: obj/shell.o obj/parser.o obj/main.o + $(CC) $^ -o $@ + +obj/%.o: src/%.cpp + $(CC) -c $(CFLAGS) $< -o $@ + +clean: + $(RM) *~ src/*~ src/#* obj/*.o bin/* diff --git a/Lab-2/bin/BinDir.txt b/Lab-2/bin/BinDir.txt new file mode 100644 index 0000000..20d1bed --- /dev/null +++ b/Lab-2/bin/BinDir.txt @@ -0,0 +1 @@ +This is the bin directory. \ No newline at end of file diff --git a/Lab-2/obj/ObjDir.txt b/Lab-2/obj/ObjDir.txt new file mode 100644 index 0000000..c395f62 --- /dev/null +++ b/Lab-2/obj/ObjDir.txt @@ -0,0 +1 @@ +This is the obj directory. \ No newline at end of file diff --git a/Lab-2/src/main.cpp b/Lab-2/src/main.cpp new file mode 100644 index 0000000..89c6b01 --- /dev/null +++ b/Lab-2/src/main.cpp @@ -0,0 +1,6 @@ +int main() +{ + Shell::Run(); + + return 0; +} \ No newline at end of file diff --git a/Lab-2/src/parser.cpp b/Lab-2/src/parser.cpp new file mode 100644 index 0000000..2facc1b --- /dev/null +++ b/Lab-2/src/parser.cpp @@ -0,0 +1,132 @@ +#include "parser.hpp" + +#include // EXIT_FAILURE +#include // printf() +#include // strlen() +#include // isspace(), +#include // strtok_r() +#include + +bool Parser::Empty(char *str) +{ + while (*str) + { + if (!isspace(*str++)) + return false; + } + return true; +} + +char *Parser::LeftTrim(char *s) +{ + while (isspace((int)*s)) + ++s; + return s; +} + +char *Parser::RightTrim(char *str) +{ + if (*str == 0) + return str; + else + { + char *back = str + strlen(str) - 1; + + while (isspace(*back)) + back--; + + *(back + 1) = 0; + + return str; + } +} + +char *Parser::Trim(char *str) +{ + if (str != nullptr) + return (LeftTrim(RightTrim(str))); + else + return nullptr; +} + +void Parser::GetArgument(char *str, const char *delim, char *argv[]) +{ + + char *token; + int i = 0; + + token = strtok(str, delim); + + while (token != nullptr) + { + argv[i] = token; + token = strtok(NULL, delim); + i++; + } + argv[i] = nullptr; +} + +void Parser::PrintArgument(char *argv[]) +{ + int i = 0; + char *s; + + while ((s = argv[i])) + { + printf(" argv[%d] = %s\n", i, s); + i++; + } +} + +void Parser::PrintArguments(char *argvs[MAX_COMMANDS][MAX_ARGV]) +{ + int i = 0; + while (argvs[i][0]) + { + printf("Command %d\n", i); + PrintArgument(argvs[i]); + i++; + } +} + +void Parser::ParseCommands(char *str, const char *delim, char *cmds[]) +{ + + char *token; + int i = 0; + + token = strtok(str, delim); + + while (token != NULL) + { + if (Empty(token)) + { + fprintf(stderr, "Parser error: EMPTY command!\n"); + exit(EXIT_FAILURE); + } + + cmds[i] = Trim(token); + token = strtok(nullptr, delim); + i++; + } + cmds[i] = nullptr; +} + +int Parser::Parse(char *str, char *argvs[MAX_COMMANDS][MAX_ARGV]) +{ + + char *cmds[MAX_COMMANDS]; + + ParseCommands(str, "|", cmds); + + int i = 0; + + while (cmds[i]) + { + GetArgument(cmds[i], " ", argvs[i]); + i++; + } + + argvs[i][0] = nullptr; + return i; +} \ No newline at end of file diff --git a/Lab-2/src/parser.hpp b/Lab-2/src/parser.hpp new file mode 100644 index 0000000..86ebc63 --- /dev/null +++ b/Lab-2/src/parser.hpp @@ -0,0 +1,23 @@ +#ifndef PARSER_HPP +#define PARSER_HPP + +const int MAX_COMMANDS = 16; +const int MAX_ARGV = 16; + +class Parser +{ +public: + static int Parse(char *str, char *argvs[MAX_COMMANDS][MAX_ARGV]); + static void PrintArguments(char *argvs[MAX_COMMANDS][MAX_ARGV]); + +private: + static void PrintArgument(char *argv[]); + static void ParseCommands(char *str, const char *delim, char *cmds[]); + static bool Empty(char *str); + static char *LeftTrim(char *s); + static char *RightTrim(char *str); + static char *Trim(char *str); + static void GetArgument(char *str, const char *delim, char *argv[]); +}; + +#endif // PARSER_HPP diff --git a/Lab-2/src/shell.cpp b/Lab-2/src/shell.cpp new file mode 100644 index 0000000..3e98ee9 --- /dev/null +++ b/Lab-2/src/shell.cpp @@ -0,0 +1,125 @@ +#include "shell.hpp" + +bool DEBUG = false; + +void DebugPrint(const char *message) +{ + if (DEBUG) + printf("%s\n", message); +} + +void Shell::ForkError() +{ + perror("fork() failed)"); + exit(EXIT_FAILURE); +} + +void Shell::CloseError() +{ + perror("Couldn't close file descriptor"); + exit(EXIT_FAILURE); +} + +void Shell::Execute(char *argv[], int number_of_commands, int read_pipe[2], int write_pipe[2], int all_pipes[][2]) +{ + pid_t pid; + + switch (pid = fork()) + { + case -1: + ForkError(); + case 0: + // if read_pipe is not null + // then duplicate read_pipe (e.g. dup2) for STDIN + // if write_pipe is not null + // then duplicate write_pipe (e.g. dup2) for STDOUT + // Close pipes. + + execvp(argv[0], argv); + perror("execvp"); + exit(EXIT_FAILURE); + + default: + char buffer[MAX_BUFFER]; + sprintf(buffer, "Pid of %s: %d\n", argv[0], pid); + DebugPrint(buffer); + break; + } +} + +void Shell::ExecuteCommands(char *argvs[MAX_COMMANDS][MAX_ARGV], const size_t &number_of_commands, int all_pipes[][2]) +{ + // 1. Cases to cover in a loop; for i = 0 to number_of_commands + // |------------------------------------------------------------------- + // | COMMAND | READ PIPE | WRITE PIPE | + // |------------------------------------------------------------------- + // | Only 1 command | null | null | + // |------------------------------------------------------------------- + // | First command | null | current pipe | + // |------------------------------------------------------------------- + // | Last command | previous pipe | null | + // |------------------------------------------------------------------- + // | Middle command | previous pipe | current pipe | + // -------------------------------------------------------------------- + + // 2. Close pipes. +} + +void Shell::GetLine(char *buffer, size_t size) +{ + getline(&buffer, &size, stdin); + buffer[strlen(buffer) - 1] = '\0'; +} + +void Shell::WaitForAllCommands(const size_t &number_of_commands) +{ + // for i = 0 to number_of_commands + // call wait() + +} + +void Shell::InitializePipes(int all_pipes[][2], const size_t &number_of_commands) +{ + // for i = 0 to number_of_commands + // init all_pipes[i] +} + +void Shell::ClosePipes(int all_pipes[][2], const size_t &number_of_commands) +{ + // for i = 0 to number_of_commands + // close read and write ends for pipes +} + +void Shell::Run() +{ + int number_of_commands = 0; + char *argvs[MAX_COMMANDS][MAX_ARGV]; + const size_t size = 128; + char line[size]; + + while (true) + { + printf(" >> "); + + Shell::GetLine(line, size); + + number_of_commands = Parser::Parse(line, argvs); + + char buffer[MAX_BUFFER]; + sprintf(buffer, "%d commands parsed.\n", number_of_commands); + DebugPrint(buffer); + + if (DEBUG) + Parser::PrintArguments(argvs); + + int(*all_pipes)[2] = new int[number_of_commands][2]; + Shell::InitializePipes(all_pipes, number_of_commands); + + ExecuteCommands(argvs, number_of_commands, all_pipes); + Shell::WaitForAllCommands(number_of_commands); + + delete[] all_pipes; + } + + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/Lab-2/src/shell.hpp b/Lab-2/src/shell.hpp new file mode 100644 index 0000000..5740723 --- /dev/null +++ b/Lab-2/src/shell.hpp @@ -0,0 +1,33 @@ +#ifndef SHELL_HPP +#define SHELL_HPP + +#include "parser.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +const int READ = 0; +const int WRITE = 1; +const int MAX_BUFFER = 128; + +class Shell +{ +public: + static void ForkError(); + static void CloseError(); + static void Execute(char *argv[], int number_of_commands, int read_pipe[2], int write_pipe[2], int all_pipes[][2]); + static void ExecuteCommands(char *argvs[MAX_COMMANDS][MAX_ARGV], const size_t &number_of_commands, int all_pipes[][2]); + static void GetLine(char *buffer, size_t size); + static void WaitForAllCommands(const size_t& n); + static void Run(); + static void InitializePipes(int all_pipes[][2], const size_t &number_of_commands); + static void ClosePipes(int all_pipes[][2], const size_t &number_of_commands); +}; + +#endif \ No newline at end of file